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