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 41AttributeDescription *nssov_pam_host_ad; 42AttributeDescription *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 */ 51void 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 */ 77int 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 */ 96int 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 108void 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 */ 134int 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 173int 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 198int 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 */ 241static 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 256int 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 */ 291static 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 */ 393static 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 467static 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 482static 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 492enum { 493 NSS_SSD=1, 494 NSS_MAP, 495 NSS_PAM, 496 NSS_PAMGROUP, 497 NSS_PAMSESS 498}; 499 500static ConfigDriver nss_cf_gen; 501 502static 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 591static 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 604static int 605nss_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 792static int 793nssov_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 826static int 827nssov_db_destroy( 828 BackendDB *be, 829 ConfigReply *cr ) 830{ 831 return 0; 832} 833 834static int 835nssov_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 989static int 990nssov_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 1020static slap_overinst nssov; 1021 1022int 1023nssov_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 1042int 1043init_module( int argc, char *argv[] ) 1044{ 1045 return nssov_initialize(); 1046} 1047#endif 1048