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