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