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