]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/nssov/nssov.c
More for session management
[openldap] / contrib / slapd-modules / nssov / nssov.c
1 /* nssov.c - nss-ldap overlay for slapd */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 2008-2009 by Howard Chu, Symas Corp.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /*
16  * This code references portions of the nss-ldapd package
17  * written by Arthur de Jong. The nss-ldapd code was forked
18  * from the nss-ldap library written by Luke Howard.
19  */
20
21 #include "nssov.h"
22
23 #ifndef SLAPD_OVER_NSSOV
24 #define SLAPD_OVER_NSSOV SLAPD_MOD_DYNAMIC
25 #endif
26
27 #include "../slapd/config.h"    /* not nss-ldapd config.h */
28
29 #include "lutil.h"
30
31 #include <ac/errno.h>
32 #include <ac/unistd.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35
36 AttributeDescription *nssov_pam_host_ad;
37 AttributeDescription *nssov_pam_svc_ad;
38
39 /* buffer sizes for I/O */
40 #define READBUFFER_MINSIZE 32
41 #define READBUFFER_MAXSIZE 64
42 #define WRITEBUFFER_MINSIZE 64
43 #define WRITEBUFFER_MAXSIZE 64*1024
44
45 /* Find the given attribute's value in the RDN of the DN */
46 int nssov_find_rdnval(struct berval *dn, AttributeDescription *ad, struct berval *value)
47 {
48         struct berval rdn;
49         char *next;
50
51         BER_BVZERO(value);
52         dnRdn( dn, &rdn );
53         do {
54                 next = ber_bvchr( &rdn, '+' );
55                 if ( rdn.bv_val[ad->ad_cname.bv_len] == '=' &&
56                         !ber_bvcmp( &rdn, &ad->ad_cname )) {
57                         if ( next )
58                                 rdn.bv_len = next - rdn.bv_val;
59                         value->bv_val = rdn.bv_val + ad->ad_cname.bv_len + 1;
60                         value->bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
61                         break;
62                 }
63                 if ( !next )
64                         break;
65                 next++;
66                 rdn.bv_len -= next - rdn.bv_val;
67                 rdn.bv_val = next;
68         } while (1);
69 }
70
71 /* create a search filter using a name that requires escaping */
72 int nssov_filter_byname(nssov_mapinfo *mi,int key,struct berval *name,struct berval *buf)
73 {
74         char buf2[1024];
75         struct berval bv2 = {sizeof(buf2),buf2};
76
77         /* escape attribute */
78         if (nssov_escape(name,&bv2))
79                 return -1;
80         /* build filter */
81         if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
82                 buf->bv_len )
83                 return -1;
84         buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
85                 mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
86                 bv2.bv_val );
87         return 0;
88 }
89
90 /* create a search filter using a string converted from an int */
91 int nssov_filter_byid(nssov_mapinfo *mi,int key,struct berval *id,struct berval *buf)
92 {
93         /* build filter */
94         if (id->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
95                 buf->bv_len )
96                 return -1;
97         buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
98                 mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
99                 id->bv_val );
100         return 0;
101 }
102
103 void get_userpassword(struct berval *attr,struct berval *pw)
104 {
105         int i;
106         /* go over the entries and return the remainder of the value if it
107                  starts with {crypt} or crypt$ */
108         for (i=0;!BER_BVISNULL(&attr[i]);i++)
109         {
110                 if (strncasecmp(attr[i].bv_val,"{crypt}",7)==0) {
111                         pw->bv_val = attr[i].bv_val + 7;
112                         pw->bv_len = attr[i].bv_len - 7;
113                         return;
114                 }
115                 if (strncasecmp(attr[i].bv_val,"crypt$",6)==0) {
116                         pw->bv_val = attr[i].bv_val + 6;
117                         pw->bv_len = attr[i].bv_len - 6;
118                         return;
119                 }
120         }
121         /* just return the first value completely */
122         *pw = *attr;
123         /* TODO: support more password formats e.g. SMD5
124                 (which is $1$ but in a different format)
125                 (any code for this is more than welcome) */
126 }
127
128 /* this writes a single address to the stream */
129 int write_address(TFILE *fp,struct berval *addr)
130 {
131         int32_t tmpint32;
132         struct in_addr ipv4addr;
133         struct in6_addr ipv6addr;
134         /* try to parse the address as IPv4 first, fall back to IPv6 */
135         if (inet_pton(AF_INET,addr->bv_val,&ipv4addr)>0)
136         {
137                 /* write address type */
138                 WRITE_INT32(fp,AF_INET);
139                 /* write the address length */
140                 WRITE_INT32(fp,sizeof(struct in_addr));
141                 /* write the address itself (in network byte order) */
142                 WRITE_TYPE(fp,ipv4addr,struct in_addr);
143         }
144         else if (inet_pton(AF_INET6,addr->bv_val,&ipv6addr)>0)
145         {
146                 /* write address type */
147                 WRITE_INT32(fp,AF_INET6);
148                 /* write the address length */
149                 WRITE_INT32(fp,sizeof(struct in6_addr));
150                 /* write the address itself (in network byte order) */
151                 WRITE_TYPE(fp,ipv6addr,struct in6_addr);
152         }
153         else
154         {
155                 /* failure, log but write simple invalid address
156                          (otherwise the address list is messed up) */
157                 /* TODO: have error message in correct format */
158                 Debug(LDAP_DEBUG_ANY,"nssov: unparseable address: %s\n",addr->bv_val,0,0);
159                 /* write an illegal address type */
160                 WRITE_INT32(fp,-1);
161                 /* write an empty address */
162                 WRITE_INT32(fp,0);
163         }
164         /* we're done */
165         return 0;
166 }
167
168 int read_address(TFILE *fp,char *addr,int *addrlen,int *af)
169 {
170         int32_t tmpint32;
171         int len;
172         /* read address family */
173         READ_INT32(fp,*af);
174         if ((*af!=AF_INET)&&(*af!=AF_INET6))
175         {
176                 Debug(LDAP_DEBUG_ANY,"nssov: incorrect address family specified: %d\n",*af,0,0);
177                 return -1;
178         }
179         /* read address length */
180         READ_INT32(fp,len);
181         if ((len>*addrlen)||(len<=0))
182         {
183                 Debug(LDAP_DEBUG_ANY,"nssov: address length incorrect: %d\n",len,0,0);
184                 return -1;
185         }
186         *addrlen=len;
187         /* read address */
188         READ(fp,addr,len);
189         /* we're done */
190         return 0;
191 }
192
193 int nssov_escape(struct berval *src,struct berval *dst)
194 {
195         size_t pos=0;
196         int i;
197         /* go over all characters in source string */
198         for (i=0;i<src->bv_len;i++)
199         {
200                 /* check if char will fit */
201                 if (pos>=(dst->bv_len-4))
202                         return -1;
203                 /* do escaping for some characters */
204                 switch (src->bv_val[i])
205                 {
206                         case '*':
207                                 strcpy(dst->bv_val+pos,"\\2a");
208                                 pos+=3;
209                                 break;
210                         case '(':
211                                 strcpy(dst->bv_val+pos,"\\28");
212                                 pos+=3;
213                                 break;
214                         case ')':
215                                 strcpy(dst->bv_val+pos,"\\29");
216                                 pos+=3;
217                                 break;
218                         case '\\':
219                                 strcpy(dst->bv_val+pos,"\\5c");
220                                 pos+=3;
221                                 break;
222                         default:
223                                 /* just copy character */
224                                 dst->bv_val[pos++]=src->bv_val[i];
225                                 break;
226                 }
227         }
228         /* terminate destination string */
229         dst->bv_val[pos]='\0';
230         dst->bv_len = pos;
231         return 0;
232 }
233
234 /* read the version information and action from the stream
235    this function returns the read action in location pointer to by action */
236 static int read_header(TFILE *fp,int32_t *action)
237 {
238   int32_t tmpint32;
239   /* read the protocol version */
240   READ_TYPE(fp,tmpint32,int32_t);
241   if (tmpint32 != (int32_t)NSLCD_VERSION)
242   {
243     Debug( LDAP_DEBUG_TRACE,"nssov: wrong nslcd version id (%d)\n",(int)tmpint32,0,0);
244     return -1;
245   }
246   /* read the request type */
247   READ(fp,action,sizeof(int32_t));
248   return 0;
249 }
250
251 /* read a request message, returns <0 in case of errors,
252    this function closes the socket */
253 static void handleconnection(nssov_info *ni,int sock,Operation *op)
254 {
255   TFILE *fp;
256   int32_t action;
257   struct timeval readtimeout,writetimeout;
258   uid_t uid;
259   gid_t gid;
260   char authid[sizeof("gidNumber=4294967295+uidNumber=424967295,cn=peercred,cn=external,cn=auth")];
261
262   /* log connection */
263   if (lutil_getpeereid(sock,&uid,&gid))
264     Debug( LDAP_DEBUG_TRACE,"nssov: connection from unknown client: %s\n",strerror(errno),0,0);
265   else
266     Debug( LDAP_DEBUG_TRACE,"nssov: connection from uid=%d gid=%d\n",
267                       (int)uid,(int)gid,0);
268
269   /* Should do authid mapping too */
270   op->o_dn.bv_len = sprintf(authid,"gidNumber=%d+uidNumber=%d,cn=peercred,cn=external,cn=auth",
271         (int)uid, (int)gid );
272   op->o_dn.bv_val = authid;
273   op->o_ndn = op->o_dn;
274
275   /* set the timeouts */
276   readtimeout.tv_sec=0; /* clients should send their request quickly */
277   readtimeout.tv_usec=500000;
278   writetimeout.tv_sec=5; /* clients could be taking some time to process the results */
279   writetimeout.tv_usec=0;
280   /* create a stream object */
281   if ((fp=tio_fdopen(sock,&readtimeout,&writetimeout,
282                      READBUFFER_MINSIZE,READBUFFER_MAXSIZE,
283                      WRITEBUFFER_MINSIZE,WRITEBUFFER_MAXSIZE))==NULL)
284   {
285     Debug( LDAP_DEBUG_ANY,"nssov: cannot create stream for writing: %s",strerror(errno),0,0);
286     (void)close(sock);
287     return;
288   }
289   /* read request */
290   if (read_header(fp,&action))
291   {
292     (void)tio_close(fp);
293     return;
294   }
295   /* handle request */
296   switch (action)
297   {
298     case NSLCD_ACTION_ALIAS_BYNAME:     (void)nssov_alias_byname(ni,fp,op); break;
299     case NSLCD_ACTION_ALIAS_ALL:        (void)nssov_alias_all(ni,fp,op); break;
300     case NSLCD_ACTION_ETHER_BYNAME:     (void)nssov_ether_byname(ni,fp,op); break;
301     case NSLCD_ACTION_ETHER_BYETHER:    (void)nssov_ether_byether(ni,fp,op); break;
302     case NSLCD_ACTION_ETHER_ALL:        (void)nssov_ether_all(ni,fp,op); break;
303     case NSLCD_ACTION_GROUP_BYNAME:     (void)nssov_group_byname(ni,fp,op); break;
304     case NSLCD_ACTION_GROUP_BYGID:      (void)nssov_group_bygid(ni,fp,op); break;
305     case NSLCD_ACTION_GROUP_BYMEMBER:   (void)nssov_group_bymember(ni,fp,op); break;
306     case NSLCD_ACTION_GROUP_ALL:        (void)nssov_group_all(ni,fp,op); break;
307     case NSLCD_ACTION_HOST_BYNAME:      (void)nssov_host_byname(ni,fp,op); break;
308     case NSLCD_ACTION_HOST_BYADDR:      (void)nssov_host_byaddr(ni,fp,op); break;
309     case NSLCD_ACTION_HOST_ALL:         (void)nssov_host_all(ni,fp,op); break;
310     case NSLCD_ACTION_NETGROUP_BYNAME:  (void)nssov_netgroup_byname(ni,fp,op); break;
311     case NSLCD_ACTION_NETWORK_BYNAME:   (void)nssov_network_byname(ni,fp,op); break;
312     case NSLCD_ACTION_NETWORK_BYADDR:   (void)nssov_network_byaddr(ni,fp,op); break;
313     case NSLCD_ACTION_NETWORK_ALL:      (void)nssov_network_all(ni,fp,op); break;
314     case NSLCD_ACTION_PASSWD_BYNAME:    (void)nssov_passwd_byname(ni,fp,op); break;
315     case NSLCD_ACTION_PASSWD_BYUID:     (void)nssov_passwd_byuid(ni,fp,op); break;
316     case NSLCD_ACTION_PASSWD_ALL:       (void)nssov_passwd_all(ni,fp,op); break;
317     case NSLCD_ACTION_PROTOCOL_BYNAME:  (void)nssov_protocol_byname(ni,fp,op); break;
318     case NSLCD_ACTION_PROTOCOL_BYNUMBER:(void)nssov_protocol_bynumber(ni,fp,op); break;
319     case NSLCD_ACTION_PROTOCOL_ALL:     (void)nssov_protocol_all(ni,fp,op); break;
320     case NSLCD_ACTION_RPC_BYNAME:       (void)nssov_rpc_byname(ni,fp,op); break;
321     case NSLCD_ACTION_RPC_BYNUMBER:     (void)nssov_rpc_bynumber(ni,fp,op); break;
322     case NSLCD_ACTION_RPC_ALL:          (void)nssov_rpc_all(ni,fp,op); break;
323     case NSLCD_ACTION_SERVICE_BYNAME:   (void)nssov_service_byname(ni,fp,op); break;
324     case NSLCD_ACTION_SERVICE_BYNUMBER: (void)nssov_service_bynumber(ni,fp,op); break;
325     case NSLCD_ACTION_SERVICE_ALL:      (void)nssov_service_all(ni,fp,op); break;
326     case NSLCD_ACTION_SHADOW_BYNAME:    if (uid==0) (void)nssov_shadow_byname(ni,fp,op); break;
327     case NSLCD_ACTION_SHADOW_ALL:       if (uid==0) (void)nssov_shadow_all(ni,fp,op); break;
328         case NSLCD_ACTION_PAM_AUTHC:            (void)pam_authc(ni,fp,op); break;
329         case NSLCD_ACTION_PAM_AUTHZ:            (void)pam_authz(ni,fp,op); break;
330         case NSLCD_ACTION_PAM_SESS_O:           if (uid==0) (void)pam_sess_o(ni,fp,op); break;
331         case NSLCD_ACTION_PAM_SESS_C:           if (uid==0) (void)pam_sess_c(ni,fp,op); break;
332         case NSLCD_ACTION_PAM_PWMOD:            (void)pam_pwmod(ni,fp,op); break;
333     default:
334       Debug( LDAP_DEBUG_ANY,"nssov: invalid request id: %d",(int)action,0,0);
335       break;
336   }
337   /* we're done with the request */
338   (void)tio_close(fp);
339   return;
340 }
341
342 /* accept a connection on the socket */
343 static void *acceptconn(void *ctx, void *arg)
344 {
345         nssov_info *ni = arg;
346         Connection conn = {0};
347         OperationBuffer opbuf;
348         Operation *op;
349         int csock;
350
351         if ( slapd_shutdown )
352                 return NULL;
353
354         {
355                 struct sockaddr_storage addr;
356                 socklen_t alen;
357                 int j;
358
359                 /* accept a new connection */
360                 alen=(socklen_t)sizeof(struct sockaddr_storage);
361                 csock=accept(ni->ni_socket,(struct sockaddr *)&addr,&alen);
362                 connection_client_enable(ni->ni_conn);
363                 if (csock<0)
364                 {
365                         if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK))
366                         {
367                                 Debug( LDAP_DEBUG_TRACE,"nssov: accept() failed (ignored): %s",strerror(errno),0,0);
368                                 return;
369                         }
370                         Debug( LDAP_DEBUG_ANY,"nssov: accept() failed: %s",strerror(errno),0,0);
371                         return;
372                 }
373                 /* make sure O_NONBLOCK is not inherited */
374                 if ((j=fcntl(csock,F_GETFL,0))<0)
375                 {
376                         Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_GETFL) failed: %s",strerror(errno),0,0);
377                         if (close(csock))
378                                 Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
379                         return;
380                 }
381                 if (fcntl(csock,F_SETFL,j&~O_NONBLOCK)<0)
382                 {
383                         Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,~O_NONBLOCK) failed: %s",strerror(errno),0,0);
384                         if (close(csock))
385                                 Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
386                         return;
387                 }
388         }
389         connection_fake_init( &conn, &opbuf, ctx );
390         op=&opbuf.ob_op;
391         conn.c_ssf = conn.c_transport_ssf = local_ssf;
392         op->o_bd = ni->ni_db;
393         op->o_tag = LDAP_REQ_SEARCH;
394
395         /* handle the connection */
396         handleconnection(ni,csock,op);
397 }
398
399 static slap_verbmasks nss_svcs[] = {
400         { BER_BVC("alias"), NM_alias },
401         { BER_BVC("ether"), NM_ether },
402         { BER_BVC("group"), NM_group },
403         { BER_BVC("host"), NM_host },
404         { BER_BVC("netgroup"), NM_netgroup },
405         { BER_BVC("network"), NM_network },
406         { BER_BVC("passwd"), NM_passwd },
407         { BER_BVC("protocol"), NM_protocol },
408         { BER_BVC("rpc"), NM_rpc },
409         { BER_BVC("service"), NM_service },
410         { BER_BVC("shadow"), NM_shadow },
411         { BER_BVNULL, 0 }
412 };
413
414 static slap_verbmasks pam_opts[] = {
415         { BER_BVC("userhost"), NI_PAM_USERHOST },
416         { BER_BVC("userservice"), NI_PAM_USERSVC },
417         { BER_BVC("usergroup"), NI_PAM_USERGRP },
418         { BER_BVC("hostservice"), NI_PAM_HOSTSVC },
419         { BER_BVC("sasl2dn"), NI_PAM_SASL2DN },
420         { BER_BVC("uid2dn"), NI_PAM_UID2DN },
421         { BER_BVNULL, 0 }
422 };
423
424 enum {
425         NSS_SSD=1,
426         NSS_MAP,
427         NSS_PAM,
428         NSS_PAMGROUP,
429 };
430
431 static ConfigDriver nss_cf_gen;
432
433 static ConfigTable nsscfg[] = {
434         { "nssov-ssd", "service> <url", 3, 3, 0, ARG_MAGIC|NSS_SSD,
435                 nss_cf_gen, "(OLcfgCtAt:3.1 NAME 'olcNssSsd' "
436                         "DESC 'URL for searches in a given service' "
437                         "EQUALITY caseIgnoreMatch "
438                         "SYNTAX OMsDirectoryString )", NULL, NULL },
439         { "nssov-map", "service> <orig> <new", 4, 4, 0, ARG_MAGIC|NSS_MAP,
440                 nss_cf_gen, "(OLcfgCtAt:3.2 NAME 'olcNssMap' "
441                         "DESC 'Map <service> lookups of <orig> attr to <new> attr' "
442                         "EQUALITY caseIgnoreMatch "
443                         "SYNTAX OMsDirectoryString )", NULL, NULL },
444         { "nssov-pam", "options", 2, 0, 0, ARG_MAGIC|NSS_PAM,
445                 nss_cf_gen, "(OLcfgCtAt:3.3 NAME 'olcNssPam' "
446                         "DESC 'PAM authentication and authorization options' "
447                         "EQUALITY caseIgnoreMatch "
448                         "SYNTAX OMsDirectoryString )", NULL, NULL },
449         { "nssov-pam-defhost", "hostname", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
450                 (void *)offsetof(struct nssov_info, ni_pam_defhost),
451                 "(OLcfgCtAt:3.4 NAME 'olcNssPamDefHost' "
452                         "DESC 'Default hostname for service checks' "
453                         "EQUALITY caseIgnoreMatch "
454                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
455         { "nssov-pam-group-dn", "DN", 2, 2, 0, ARG_MAGIC|ARG_DN|NSS_PAMGROUP,
456                 nss_cf_gen, "(OLcfgCtAt:3.5 NAME 'olcNssPamGroupDN' "
457                         "DESC 'DN of group in which membership is required' "
458                         "EQUALITY distinguishedNameMatch "
459                         "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
460         { "nssov-pam-group-ad", "options", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
461                 (void *)offsetof(struct nssov_info, ni_pam_group_ad),
462                 "(OLcfgCtAt:3.6 NAME 'olcNssPamGroupAD' "
463                         "DESC 'Member attribute to use for group check' "
464                         "EQUALITY caseIgnoreMatch "
465                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
466         { "nssov-pam-min-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
467                 (void *)offsetof(struct nssov_info, ni_pam_min_uid),
468                 "(OLcfgCtAt:3.7 NAME 'olcNssPamMinUid' "
469                         "DESC 'Minimum UID allowed to login' "
470                         "EQUALITY integerMatch "
471                         "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
472         { "nssov-pam-max-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
473                 (void *)offsetof(struct nssov_info, ni_pam_max_uid),
474                 "(OLcfgCtAt:3.8 NAME 'olcNssPamMaxUid' "
475                         "DESC 'Maximum UID allowed to login' "
476                         "EQUALITY integerMatch "
477                         "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
478         { "nssov-pam-template-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
479                 (void *)offsetof(struct nssov_info, ni_pam_template_ad),
480                 "(OLcfgCtAt:3.9 NAME 'olcNssPamTemplateAD' "
481                         "DESC 'Attribute to use for template login name' "
482                         "EQUALITY caseIgnoreMatch "
483                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
484         { "nssov-pam-template", "name", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
485                 (void *)offsetof(struct nssov_info, ni_pam_template),
486                 "(OLcfgCtAt:3.10 NAME 'olcNssPamTemplate' "
487                         "DESC 'Default template login name' "
488                         "EQUALITY caseIgnoreMatch "
489                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
490         { NULL, NULL, 0,0,0, ARG_IGNORED }
491 };
492
493 static ConfigOCs nssocs[] = {
494         { "( OLcfgCtOc:3.1 "
495                 "NAME 'olcNssOvConfig' "
496                 "DESC 'NSS lookup configuration' "
497                 "SUP olcOverlayConfig "
498                 "MAY ( olcNssSsd $ olcNssMap $ olcNssPam $ olcNssPamDefHost $ "
499                         "olcNssPamGroupDN $ olcNssPamGroupAD $ "
500                         "olcNssPamMinUid $ olcNssPamMaxUid $ "
501                         "olcNssPamTemplateAD $ olcNssPamTemplate ) )",
502                 Cft_Overlay, nsscfg },
503         { NULL, 0, NULL }
504 };
505
506 static int
507 nss_cf_gen(ConfigArgs *c)
508 {
509         slap_overinst *on = (slap_overinst *)c->bi;
510         nssov_info *ni = on->on_bi.bi_private;
511         nssov_mapinfo *mi;
512         int i, j, rc = 0;
513         slap_mask_t m;
514
515         if ( c->op == SLAP_CONFIG_EMIT ) {
516                 switch(c->type) {
517                 case NSS_SSD:
518                         rc = 1;
519                         for (i=NM_alias;i<NM_NONE;i++) {
520                                 struct berval scope;
521                                 struct berval ssd;
522                                 struct berval base;
523
524                                 mi = &ni->ni_maps[i];
525
526                                 /* ignore all-default services */
527                                 if ( mi->mi_scope == LDAP_SCOPE_DEFAULT &&
528                                         bvmatch( &mi->mi_filter, &mi->mi_filter0 ) &&
529                                         BER_BVISNULL( &mi->mi_base ))
530                                         continue;
531
532                                 if ( BER_BVISNULL( &mi->mi_base ))
533                                         base = ni->ni_db->be_nsuffix[0];
534                                 else
535                                         base = mi->mi_base;
536                                 ldap_pvt_scope2bv(mi->mi_scope == LDAP_SCOPE_DEFAULT ?
537                                         LDAP_SCOPE_SUBTREE : mi->mi_scope, &scope);
538                                 ssd.bv_len = STRLENOF(" ldap:///???") + nss_svcs[i].word.bv_len +
539                                         base.bv_len + scope.bv_len + mi->mi_filter.bv_len;
540                                 ssd.bv_val = ch_malloc( ssd.bv_len + 1 );
541                                 sprintf(ssd.bv_val, "%s ldap:///%s??%s?%s", nss_svcs[i].word.bv_val,
542                                         base.bv_val, scope.bv_val, mi->mi_filter.bv_val );
543                                 ber_bvarray_add( &c->rvalue_vals, &ssd );
544                                 rc = 0;
545                         }
546                         break;
547                 case NSS_MAP:
548                         rc = 1;
549                         for (i=NM_alias;i<NM_NONE;i++) {
550
551                                 mi = &ni->ni_maps[i];
552                                 for (j=0;!BER_BVISNULL(&mi->mi_attrkeys[j]);j++) {
553                                         if ( ber_bvstrcasecmp(&mi->mi_attrkeys[j],
554                                                 &mi->mi_attrs[j].an_name)) {
555                                                 struct berval map;
556
557                                                 map.bv_len = nss_svcs[i].word.bv_len +
558                                                         mi->mi_attrkeys[j].bv_len +
559                                                         mi->mi_attrs[j].an_desc->ad_cname.bv_len + 2;
560                                                 map.bv_val = ch_malloc(map.bv_len + 1);
561                                                 sprintf(map.bv_val, "%s %s %s", nss_svcs[i].word.bv_val,
562                                                         mi->mi_attrkeys[j].bv_val, mi->mi_attrs[j].an_desc->ad_cname.bv_val );
563                                                 ber_bvarray_add( &c->rvalue_vals, &map );
564                                                 rc = 0;
565                                         }
566                                 }
567                         }
568                         break;
569                 case NSS_PAM:
570                         rc = mask_to_verbs( pam_opts, ni->ni_pam_opts, &c->rvalue_vals );
571                         break;
572                 case NSS_PAMGROUP:
573                         if (!BER_BVISEMPTY( &ni->ni_pam_group_dn )) {
574                                 value_add_one( &c->rvalue_vals, &ni->ni_pam_group_dn );
575                                 value_add_one( &c->rvalue_nvals, &ni->ni_pam_group_dn );
576                         } else {
577                                 rc = 1;
578                         }
579                         break;
580                 }
581                 return rc;
582         } else if ( c->op == LDAP_MOD_DELETE ) {
583                 /* FIXME */
584                 return 1;
585         }
586         switch( c->type ) {
587         case NSS_SSD: {
588                 LDAPURLDesc *lud;
589
590                 i = verb_to_mask(c->argv[1], nss_svcs);
591                 if ( i == NM_NONE )
592                         return 1;
593
594                 mi = &ni->ni_maps[i];
595                 rc = ldap_url_parse(c->argv[2], &lud);
596                 if ( rc )
597                         return 1;
598                 do {
599                         struct berval base;
600                         /* Must be LDAP scheme */
601                         if (strcasecmp(lud->lud_scheme,"ldap")) {
602                                 rc = 1;
603                                 break;
604                         }
605                         /* Host part, attrs, and extensions must be empty */
606                         if (( lud->lud_host && *lud->lud_host ) ||
607                                 lud->lud_attrs || lud->lud_exts ) {
608                                 rc = 1;
609                                 break;
610                         }
611                         ber_str2bv( lud->lud_dn,0,0,&base);
612                         rc = dnNormalize( 0,NULL,NULL,&base,&mi->mi_base,NULL);
613                         if ( rc )
614                                 break;
615                         if ( lud->lud_filter ) {
616                                 /* steal this */
617                                 ber_str2bv( lud->lud_filter,0,0,&mi->mi_filter);
618                                 lud->lud_filter = NULL;
619                         }
620                         mi->mi_scope = lud->lud_scope;
621                 } while(0);
622                 ldap_free_urldesc( lud );
623                 }
624                 break;
625         case NSS_MAP:
626                 i = verb_to_mask(c->argv[1], nss_svcs);
627                 if ( i == NM_NONE )
628                         return 1;
629                 rc = 1;
630                 mi = &ni->ni_maps[i];
631                 for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
632                         if (!strcasecmp(c->argv[2],mi->mi_attrkeys[j].bv_val)) {
633                                 AttributeDescription *ad = NULL;
634                                 const char *text;
635                                 rc = slap_str2ad( c->argv[3], &ad, &text);
636                                 if ( rc == 0 ) {
637                                         mi->mi_attrs[j].an_desc = ad;
638                                         mi->mi_attrs[j].an_name = ad->ad_cname;
639                                 }
640                                 break;
641                         }
642                 }
643                 break;
644         case NSS_PAM:
645                 m = ni->ni_pam_opts;
646                 i = verbs_to_mask(c->argc, c->argv, pam_opts, &m);
647                 if (i == 0) {
648                         ni->ni_pam_opts = m;
649                         if ((m & NI_PAM_USERHOST) && !nssov_pam_host_ad) {
650                                 const char *text;
651                                 i = slap_str2ad("host", &nssov_pam_host_ad, &text);
652                                 if (i != LDAP_SUCCESS) {
653                                         snprintf(c->cr_msg, sizeof(c->cr_msg),
654                                                 "nssov: host attr unknown: %s", text);
655                                         Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg,0,0);
656                                         rc = 1;
657                                         break;
658                                 }
659                         }
660                         if ((m & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) && !nssov_pam_svc_ad) {
661                                 const char *text;
662                                 i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
663                                 if (i != LDAP_SUCCESS) {
664                                         snprintf(c->cr_msg, sizeof(c->cr_msg),
665                                                 "nssov: authorizedService attr unknown: %s", text);
666                                         Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg,0,0);
667                                         rc = 1;
668                                         break;
669                                 }
670                         }
671                 } else {
672                         rc = 1;
673                 }
674                 break;
675         case NSS_PAMGROUP:
676                 ni->ni_pam_group_dn = c->value_ndn;
677                 ch_free( c->value_dn.bv_val );
678                 break;
679         }
680         return rc;
681 }
682
683 static int
684 nssov_db_init(
685         BackendDB *be,
686         ConfigReply *cr )
687 {
688         slap_overinst *on = (slap_overinst *)be->bd_info;
689         nssov_info *ni;
690         nssov_mapinfo *mi;
691         int i, j;
692
693         ni = ch_malloc( sizeof(nssov_info) );
694         on->on_bi.bi_private = ni;
695
696         /* set up map keys */
697         nssov_alias_init(ni);
698         nssov_ether_init(ni);
699         nssov_group_init(ni);
700         nssov_host_init(ni);
701         nssov_netgroup_init(ni);
702         nssov_network_init(ni);
703         nssov_passwd_init(ni);
704         nssov_protocol_init(ni);
705         nssov_rpc_init(ni);
706         nssov_service_init(ni);
707         nssov_shadow_init(ni);
708
709         ni->ni_db = be->bd_self;
710
711         return 0;
712 }
713
714 static int
715 nssov_db_destroy(
716         BackendDB *be,
717         ConfigReply *cr )
718 {
719 }
720
721 static int
722 nssov_db_open(
723         BackendDB *be,
724         ConfigReply *cr )
725 {
726         slap_overinst *on = (slap_overinst *)be->bd_info;
727         nssov_info *ni = on->on_bi.bi_private;
728         nssov_mapinfo *mi;
729
730         int i, sock;
731         struct sockaddr_un addr;
732
733         /* Set default bases */
734         for (i=0; i<NM_NONE; i++) {
735                 if ( BER_BVISNULL( &ni->ni_maps[i].mi_base )) {
736                         ber_dupbv( &ni->ni_maps[i].mi_base, &be->be_nsuffix[0] );
737                 }
738                 if ( ni->ni_maps[i].mi_scope == LDAP_SCOPE_DEFAULT )
739                         ni->ni_maps[i].mi_scope = LDAP_SCOPE_SUBTREE;
740         }
741         /* validate attribute maps */
742         mi = ni->ni_maps;
743         for ( i=0; i<NM_NONE; i++,mi++) {
744                 const char *text;
745                 int j;
746                 for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
747                         /* skip attrs we already validated */
748                         if ( mi->mi_attrs[j].an_desc ) continue;
749                         if ( slap_bv2ad( &mi->mi_attrs[j].an_name,
750                                 &mi->mi_attrs[j].an_desc, &text )) {
751                                 Debug(LDAP_DEBUG_ANY,"nssov: invalid attr \"%s\": %s\n",
752                                         mi->mi_attrs[j].an_name.bv_val, text, 0 );
753                                 return -1;
754                         }
755                 }
756                 BER_BVZERO(&mi->mi_attrs[j].an_name);
757                 mi->mi_attrs[j].an_desc = NULL;
758         }
759
760         /* Find host and authorizedService definitions */
761         if ((ni->ni_pam_opts & NI_PAM_USERHOST) && !nssov_pam_host_ad)
762         {
763                 const char *text;
764                 i = slap_str2ad("host", &nssov_pam_host_ad, &text);
765                 if (i != LDAP_SUCCESS) {
766                         Debug(LDAP_DEBUG_ANY,"nssov: host attr unknown: %s\n",
767                                 text, 0, 0 );
768                         return -1;
769                 }
770         }
771         if ((ni->ni_pam_opts & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) &&
772                 !nssov_pam_svc_ad)
773         {
774                 const char *text;
775                 i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
776                 if (i != LDAP_SUCCESS) {
777                         Debug(LDAP_DEBUG_ANY,"nssov: authorizedService attr unknown: %s\n",
778                                 text, 0, 0 );
779                         return -1;
780                 }
781         }
782         if ( slapMode & SLAP_SERVER_MODE ) {
783                 /* create a socket */
784                 if ( (sock=socket(PF_UNIX,SOCK_STREAM,0))<0 )
785                 {
786                         Debug(LDAP_DEBUG_ANY,"nssov: cannot create socket: %s",strerror(errno),0,0);
787                         return -1;
788                 }
789                 /* remove existing named socket */
790                 if (unlink(NSLCD_SOCKET)<0)
791                 {
792                         Debug( LDAP_DEBUG_TRACE,"nssov: unlink() of "NSLCD_SOCKET" failed (ignored): %s",
793                                                         strerror(errno),0,0);
794                 }
795                 /* create socket address structure */
796                 memset(&addr,0,sizeof(struct sockaddr_un));
797                 addr.sun_family=AF_UNIX;
798                 strncpy(addr.sun_path,NSLCD_SOCKET,sizeof(addr.sun_path));
799                 addr.sun_path[sizeof(addr.sun_path)-1]='\0';
800                 /* bind to the named socket */
801                 if (bind(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr_un)))
802                 {
803                         Debug( LDAP_DEBUG_ANY,"nssov: bind() to "NSLCD_SOCKET" failed: %s",
804                                                         strerror(errno),0,0);
805                         if (close(sock))
806                                 Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
807                         return -1;
808                 }
809                 /* close the file descriptor on exit */
810                 if (fcntl(sock,F_SETFD,FD_CLOEXEC)<0)
811                 {
812                         Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,O_NONBLOCK) failed: %s",strerror(errno),0,0);
813                         if (close(sock))
814                                 Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
815                         return -1;
816                 }
817                 /* set permissions of socket so anybody can do requests */
818                 /* Note: we use chmod() here instead of fchmod() because
819                          fchmod does not work on sockets
820                          http://www.opengroup.org/onlinepubs/009695399/functions/fchmod.html
821                          http://lkml.org/lkml/2005/5/16/11 */
822                 if (chmod(NSLCD_SOCKET,(mode_t)0666))
823                 {
824                         Debug( LDAP_DEBUG_ANY,"nssov: chmod(0666) failed: %s",strerror(errno),0,0);
825                         if (close(sock))
826                                 Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
827                         return -1;
828                 }
829                 /* start listening for connections */
830                 if (listen(sock,SOMAXCONN)<0)
831                 {
832                         Debug( LDAP_DEBUG_ANY,"nssov: listen() failed: %s",strerror(errno),0,0);
833                         if (close(sock))
834                                 Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
835                         return -1;
836                 }
837                 ni->ni_socket = sock;
838                 ni->ni_conn = connection_client_setup( sock, acceptconn, ni );
839         }
840
841         return 0;
842 }
843
844 static int
845 nssov_db_close(
846         BackendDB *be,
847         ConfigReply *cr )
848 {
849         slap_overinst *on = (slap_overinst *)be->bd_info;
850         nssov_info *ni = on->on_bi.bi_private;
851
852         /* close socket if it's still in use */
853         if (ni->ni_socket >= 0);
854         {
855                 if (close(ni->ni_socket))
856                         Debug( LDAP_DEBUG_ANY,"problem closing server socket (ignored): %s",strerror(errno),0,0);
857                 ni->ni_socket = -1;
858         }
859         /* remove existing named socket */
860         if (unlink(NSLCD_SOCKET)<0)
861         {
862                 Debug( LDAP_DEBUG_TRACE,"unlink() of "NSLCD_SOCKET" failed (ignored): %s",
863                         strerror(errno),0,0);
864         }
865 }
866
867 static slap_overinst nssov;
868
869 int
870 nssov_initialize( void )
871 {
872         int rc;
873
874         nssov.on_bi.bi_type = "nssov";
875         nssov.on_bi.bi_db_init = nssov_db_init;
876         nssov.on_bi.bi_db_destroy = nssov_db_destroy;
877         nssov.on_bi.bi_db_open = nssov_db_open;
878         nssov.on_bi.bi_db_close = nssov_db_close;
879
880         nssov.on_bi.bi_cf_ocs = nssocs;
881
882         rc = config_register_schema( nsscfg, nssocs );
883         if ( rc ) return rc;
884
885         return overlay_register(&nssov);
886 }
887
888 #if SLAPD_OVER_NSSOV == SLAPD_MOD_DYNAMIC
889 int
890 init_module( int argc, char *argv[] )
891 {
892         return nssov_initialize();
893 }
894 #endif