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