Back to index

openldap  2.4.31
nssov.c
Go to the documentation of this file.
00001 /* nssov.c - nss-ldap overlay for slapd */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 
00004  *
00005  * Copyright 2008-2012 The OpenLDAP Foundation.
00006  * Portions Copyright 2008 by Howard Chu, Symas Corp.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This code references portions of the nss-ldapd package
00019  * written by Arthur de Jong. The nss-ldapd code was forked
00020  * from the nss-ldap library written by Luke Howard.
00021  */
00022 
00023 #include "nssov.h"
00024 
00025 #ifndef SLAPD_OVER_NSSOV
00026 #define SLAPD_OVER_NSSOV SLAPD_MOD_DYNAMIC
00027 #endif
00028 
00029 #include "../slapd/config.h"       /* not nss-ldapd config.h */
00030 
00031 #include "lutil.h"
00032 
00033 #include <ac/errno.h>
00034 #include <ac/unistd.h>
00035 #include <fcntl.h>
00036 #include <sys/stat.h>
00037 
00038 AttributeDescription *nssov_pam_host_ad;
00039 AttributeDescription *nssov_pam_svc_ad;
00040 
00041 /* buffer sizes for I/O */
00042 #define READBUFFER_MINSIZE 32
00043 #define READBUFFER_MAXSIZE 64
00044 #define WRITEBUFFER_MINSIZE 64
00045 #define WRITEBUFFER_MAXSIZE 64*1024
00046 
00047 /* Find the given attribute's value in the RDN of the DN */
00048 int nssov_find_rdnval(struct berval *dn, AttributeDescription *ad, struct berval *value)
00049 {
00050        struct berval rdn;
00051        char *next;
00052 
00053        BER_BVZERO(value);
00054        dnRdn( dn, &rdn );
00055        do {
00056               next = ber_bvchr( &rdn, '+' );
00057               if ( rdn.bv_val[ad->ad_cname.bv_len] == '=' &&
00058                      !ber_bvcmp( &rdn, &ad->ad_cname )) {
00059                      if ( next )
00060                             rdn.bv_len = next - rdn.bv_val;
00061                      value->bv_val = rdn.bv_val + ad->ad_cname.bv_len + 1;
00062                      value->bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
00063                      break;
00064               }
00065               if ( !next )
00066                      break;
00067               next++;
00068               rdn.bv_len -= next - rdn.bv_val;
00069               rdn.bv_val = next;
00070        } while (1);
00071 }
00072 
00073 /* create a search filter using a name that requires escaping */
00074 int nssov_filter_byname(nssov_mapinfo *mi,int key,struct berval *name,struct berval *buf)
00075 {
00076        char buf2[1024];
00077        struct berval bv2 = {sizeof(buf2),buf2};
00078 
00079        /* escape attribute */
00080        if (nssov_escape(name,&bv2))
00081               return -1;
00082        /* build filter */
00083        if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
00084               buf->bv_len )
00085               return -1;
00086        buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
00087               mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
00088               bv2.bv_val );
00089        return 0;
00090 }
00091 
00092 /* create a search filter using a string converted from an int */
00093 int nssov_filter_byid(nssov_mapinfo *mi,int key,struct berval *id,struct berval *buf)
00094 {
00095        /* build filter */
00096        if (id->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
00097               buf->bv_len )
00098               return -1;
00099        buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
00100               mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
00101               id->bv_val );
00102        return 0;
00103 }
00104 
00105 void get_userpassword(struct berval *attr,struct berval *pw)
00106 {
00107        int i;
00108        /* go over the entries and return the remainder of the value if it
00109                starts with {crypt} or crypt$ */
00110        for (i=0;!BER_BVISNULL(&attr[i]);i++)
00111        {
00112               if (strncasecmp(attr[i].bv_val,"{crypt}",7)==0) {
00113                      pw->bv_val = attr[i].bv_val + 7;
00114                      pw->bv_len = attr[i].bv_len - 7;
00115                      return;
00116               }
00117               if (strncasecmp(attr[i].bv_val,"crypt$",6)==0) {
00118                      pw->bv_val = attr[i].bv_val + 6;
00119                      pw->bv_len = attr[i].bv_len - 6;
00120                      return;
00121               }
00122        }
00123        /* just return the first value completely */
00124        *pw = *attr;
00125        /* TODO: support more password formats e.g. SMD5
00126               (which is $1$ but in a different format)
00127               (any code for this is more than welcome) */
00128 }
00129 
00130 /* this writes a single address to the stream */
00131 int write_address(TFILE *fp,struct berval *addr)
00132 {
00133        int32_t tmpint32;
00134        struct in_addr ipv4addr;
00135        struct in6_addr ipv6addr;
00136        /* try to parse the address as IPv4 first, fall back to IPv6 */
00137        if (inet_pton(AF_INET,addr->bv_val,&ipv4addr)>0)
00138        {
00139               /* write address type */
00140               WRITE_INT32(fp,AF_INET);
00141               /* write the address length */
00142               WRITE_INT32(fp,sizeof(struct in_addr));
00143               /* write the address itself (in network byte order) */
00144               WRITE_TYPE(fp,ipv4addr,struct in_addr);
00145        }
00146        else if (inet_pton(AF_INET6,addr->bv_val,&ipv6addr)>0)
00147        {
00148               /* write address type */
00149               WRITE_INT32(fp,AF_INET6);
00150               /* write the address length */
00151               WRITE_INT32(fp,sizeof(struct in6_addr));
00152               /* write the address itself (in network byte order) */
00153               WRITE_TYPE(fp,ipv6addr,struct in6_addr);
00154        }
00155        else
00156        {
00157               /* failure, log but write simple invalid address
00158                       (otherwise the address list is messed up) */
00159               /* TODO: have error message in correct format */
00160               Debug(LDAP_DEBUG_ANY,"nssov: unparseable address: %s\n",addr->bv_val,0,0);
00161               /* write an illegal address type */
00162               WRITE_INT32(fp,-1);
00163               /* write an empty address */
00164               WRITE_INT32(fp,0);
00165        }
00166        /* we're done */
00167        return 0;
00168 }
00169 
00170 int read_address(TFILE *fp,char *addr,int *addrlen,int *af)
00171 {
00172        int32_t tmpint32;
00173        int len;
00174        /* read address family */
00175        READ_INT32(fp,*af);
00176        if ((*af!=AF_INET)&&(*af!=AF_INET6))
00177        {
00178               Debug(LDAP_DEBUG_ANY,"nssov: incorrect address family specified: %d\n",*af,0,0);
00179               return -1;
00180        }
00181        /* read address length */
00182        READ_INT32(fp,len);
00183        if ((len>*addrlen)||(len<=0))
00184        {
00185               Debug(LDAP_DEBUG_ANY,"nssov: address length incorrect: %d\n",len,0,0);
00186               return -1;
00187        }
00188        *addrlen=len;
00189        /* read address */
00190        READ(fp,addr,len);
00191        /* we're done */
00192        return 0;
00193 }
00194 
00195 int nssov_escape(struct berval *src,struct berval *dst)
00196 {
00197        size_t pos=0;
00198        int i;
00199        /* go over all characters in source string */
00200        for (i=0;i<src->bv_len;i++)
00201        {
00202               /* check if char will fit */
00203               if (pos>=(dst->bv_len-4))
00204                      return -1;
00205               /* do escaping for some characters */
00206               switch (src->bv_val[i])
00207               {
00208                      case '*':
00209                             strcpy(dst->bv_val+pos,"\\2a");
00210                             pos+=3;
00211                             break;
00212                      case '(':
00213                             strcpy(dst->bv_val+pos,"\\28");
00214                             pos+=3;
00215                             break;
00216                      case ')':
00217                             strcpy(dst->bv_val+pos,"\\29");
00218                             pos+=3;
00219                             break;
00220                      case '\\':
00221                             strcpy(dst->bv_val+pos,"\\5c");
00222                             pos+=3;
00223                             break;
00224                      default:
00225                             /* just copy character */
00226                             dst->bv_val[pos++]=src->bv_val[i];
00227                             break;
00228               }
00229        }
00230        /* terminate destination string */
00231        dst->bv_val[pos]='\0';
00232        dst->bv_len = pos;
00233        return 0;
00234 }
00235 
00236 /* read the version information and action from the stream
00237    this function returns the read action in location pointer to by action */
00238 static int read_header(TFILE *fp,int32_t *action)
00239 {
00240   int32_t tmpint32;
00241   /* read the protocol version */
00242   READ_TYPE(fp,tmpint32,int32_t);
00243   if (tmpint32 != (int32_t)NSLCD_VERSION)
00244   {
00245     Debug( LDAP_DEBUG_TRACE,"nssov: wrong nslcd version id (%d)\n",(int)tmpint32,0,0);
00246     return -1;
00247   }
00248   /* read the request type */
00249   READ(fp,action,sizeof(int32_t));
00250   return 0;
00251 }
00252 
00253 /* read a request message, returns <0 in case of errors,
00254    this function closes the socket */
00255 static void handleconnection(nssov_info *ni,int sock,Operation *op)
00256 {
00257   TFILE *fp;
00258   int32_t action;
00259   struct timeval readtimeout,writetimeout;
00260   uid_t uid;
00261   gid_t gid;
00262   char authid[sizeof("gidNumber=4294967295+uidNumber=424967295,cn=peercred,cn=external,cn=auth")];
00263   char peerbuf[8];
00264   struct berval peerbv = { sizeof(peerbuf), peerbuf };
00265 
00266   /* log connection */
00267   if (LUTIL_GETPEEREID(sock,&uid,&gid,&peerbv))
00268     Debug( LDAP_DEBUG_TRACE,"nssov: connection from unknown client: %s\n",strerror(errno),0,0);
00269   else
00270     Debug( LDAP_DEBUG_TRACE,"nssov: connection from uid=%d gid=%d\n",
00271                       (int)uid,(int)gid,0);
00272 
00273   /* Should do authid mapping too */
00274   op->o_dn.bv_len = sprintf(authid,"gidNumber=%d+uidNumber=%d,cn=peercred,cn=external,cn=auth",
00275        (int)uid, (int)gid );
00276   op->o_dn.bv_val = authid;
00277   op->o_ndn = op->o_dn;
00278 
00279   /* set the timeouts */
00280   readtimeout.tv_sec=0; /* clients should send their request quickly */
00281   readtimeout.tv_usec=500000;
00282   writetimeout.tv_sec=5; /* clients could be taking some time to process the results */
00283   writetimeout.tv_usec=0;
00284   /* create a stream object */
00285   if ((fp=tio_fdopen(sock,&readtimeout,&writetimeout,
00286                      READBUFFER_MINSIZE,READBUFFER_MAXSIZE,
00287                      WRITEBUFFER_MINSIZE,WRITEBUFFER_MAXSIZE))==NULL)
00288   {
00289     Debug( LDAP_DEBUG_ANY,"nssov: cannot create stream for writing: %s",strerror(errno),0,0);
00290     (void)close(sock);
00291     return;
00292   }
00293   /* read request */
00294   if (read_header(fp,&action))
00295   {
00296     (void)tio_close(fp);
00297     return;
00298   }
00299   /* handle request */
00300   switch (action)
00301   {
00302     case NSLCD_ACTION_ALIAS_BYNAME:     (void)nssov_alias_byname(ni,fp,op); break;
00303     case NSLCD_ACTION_ALIAS_ALL:        (void)nssov_alias_all(ni,fp,op); break;
00304     case NSLCD_ACTION_ETHER_BYNAME:     (void)nssov_ether_byname(ni,fp,op); break;
00305     case NSLCD_ACTION_ETHER_BYETHER:    (void)nssov_ether_byether(ni,fp,op); break;
00306     case NSLCD_ACTION_ETHER_ALL:        (void)nssov_ether_all(ni,fp,op); break;
00307     case NSLCD_ACTION_GROUP_BYNAME:     (void)nssov_group_byname(ni,fp,op); break;
00308     case NSLCD_ACTION_GROUP_BYGID:      (void)nssov_group_bygid(ni,fp,op); break;
00309     case NSLCD_ACTION_GROUP_BYMEMBER:   (void)nssov_group_bymember(ni,fp,op); break;
00310     case NSLCD_ACTION_GROUP_ALL:        (void)nssov_group_all(ni,fp,op); break;
00311     case NSLCD_ACTION_HOST_BYNAME:      (void)nssov_host_byname(ni,fp,op); break;
00312     case NSLCD_ACTION_HOST_BYADDR:      (void)nssov_host_byaddr(ni,fp,op); break;
00313     case NSLCD_ACTION_HOST_ALL:         (void)nssov_host_all(ni,fp,op); break;
00314     case NSLCD_ACTION_NETGROUP_BYNAME:  (void)nssov_netgroup_byname(ni,fp,op); break;
00315     case NSLCD_ACTION_NETWORK_BYNAME:   (void)nssov_network_byname(ni,fp,op); break;
00316     case NSLCD_ACTION_NETWORK_BYADDR:   (void)nssov_network_byaddr(ni,fp,op); break;
00317     case NSLCD_ACTION_NETWORK_ALL:      (void)nssov_network_all(ni,fp,op); break;
00318     case NSLCD_ACTION_PASSWD_BYNAME:    (void)nssov_passwd_byname(ni,fp,op); break;
00319     case NSLCD_ACTION_PASSWD_BYUID:     (void)nssov_passwd_byuid(ni,fp,op); break;
00320     case NSLCD_ACTION_PASSWD_ALL:       (void)nssov_passwd_all(ni,fp,op); break;
00321     case NSLCD_ACTION_PROTOCOL_BYNAME:  (void)nssov_protocol_byname(ni,fp,op); break;
00322     case NSLCD_ACTION_PROTOCOL_BYNUMBER:(void)nssov_protocol_bynumber(ni,fp,op); break;
00323     case NSLCD_ACTION_PROTOCOL_ALL:     (void)nssov_protocol_all(ni,fp,op); break;
00324     case NSLCD_ACTION_RPC_BYNAME:       (void)nssov_rpc_byname(ni,fp,op); break;
00325     case NSLCD_ACTION_RPC_BYNUMBER:     (void)nssov_rpc_bynumber(ni,fp,op); break;
00326     case NSLCD_ACTION_RPC_ALL:          (void)nssov_rpc_all(ni,fp,op); break;
00327     case NSLCD_ACTION_SERVICE_BYNAME:   (void)nssov_service_byname(ni,fp,op); break;
00328     case NSLCD_ACTION_SERVICE_BYNUMBER: (void)nssov_service_bynumber(ni,fp,op); break;
00329     case NSLCD_ACTION_SERVICE_ALL:      (void)nssov_service_all(ni,fp,op); break;
00330     case NSLCD_ACTION_SHADOW_BYNAME:    if (uid==0) (void)nssov_shadow_byname(ni,fp,op); break;
00331     case NSLCD_ACTION_SHADOW_ALL:       if (uid==0) (void)nssov_shadow_all(ni,fp,op); break;
00332        case NSLCD_ACTION_PAM_AUTHC:              (void)pam_authc(ni,fp,op); break;
00333        case NSLCD_ACTION_PAM_AUTHZ:              (void)pam_authz(ni,fp,op); break;
00334        case NSLCD_ACTION_PAM_SESS_O:             if (uid==0) (void)pam_sess_o(ni,fp,op); break;
00335        case NSLCD_ACTION_PAM_SESS_C:             if (uid==0) (void)pam_sess_c(ni,fp,op); break;
00336        case NSLCD_ACTION_PAM_PWMOD:              (void)pam_pwmod(ni,fp,op); break;
00337     default:
00338       Debug( LDAP_DEBUG_ANY,"nssov: invalid request id: %d",(int)action,0,0);
00339       break;
00340   }
00341   /* we're done with the request */
00342   (void)tio_close(fp);
00343   return;
00344 }
00345 
00346 /* accept a connection on the socket */
00347 static void *acceptconn(void *ctx, void *arg)
00348 {
00349        nssov_info *ni = arg;
00350        Connection conn = {0};
00351        OperationBuffer opbuf;
00352        Operation *op;
00353        int csock;
00354 
00355        if ( slapd_shutdown )
00356               return NULL;
00357 
00358        {
00359               struct sockaddr_storage addr;
00360               socklen_t alen;
00361               int j;
00362 
00363               /* accept a new connection */
00364               alen=(socklen_t)sizeof(struct sockaddr_storage);
00365               csock=accept(ni->ni_socket,(struct sockaddr *)&addr,&alen);
00366               connection_client_enable(ni->ni_conn);
00367               if (csock<0)
00368               {
00369                      if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK))
00370                      {
00371                             Debug( LDAP_DEBUG_TRACE,"nssov: accept() failed (ignored): %s",strerror(errno),0,0);
00372                             return;
00373                      }
00374                      Debug( LDAP_DEBUG_ANY,"nssov: accept() failed: %s",strerror(errno),0,0);
00375                      return;
00376               }
00377               /* make sure O_NONBLOCK is not inherited */
00378               if ((j=fcntl(csock,F_GETFL,0))<0)
00379               {
00380                      Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_GETFL) failed: %s",strerror(errno),0,0);
00381                      if (close(csock))
00382                             Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
00383                      return;
00384               }
00385               if (fcntl(csock,F_SETFL,j&~O_NONBLOCK)<0)
00386               {
00387                      Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,~O_NONBLOCK) failed: %s",strerror(errno),0,0);
00388                      if (close(csock))
00389                             Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
00390                      return;
00391               }
00392        }
00393        connection_fake_init( &conn, &opbuf, ctx );
00394        op=&opbuf.ob_op;
00395        conn.c_ssf = conn.c_transport_ssf = local_ssf;
00396        op->o_bd = ni->ni_db;
00397        op->o_tag = LDAP_REQ_SEARCH;
00398 
00399        /* handle the connection */
00400        handleconnection(ni,csock,op);
00401 }
00402 
00403 static slap_verbmasks nss_svcs[] = {
00404        { BER_BVC("aliases"), NM_alias },
00405        { BER_BVC("ethers"), NM_ether },
00406        { BER_BVC("group"), NM_group },
00407        { BER_BVC("hosts"), NM_host },
00408        { BER_BVC("netgroup"), NM_netgroup },
00409        { BER_BVC("networks"), NM_network },
00410        { BER_BVC("passwd"), NM_passwd },
00411        { BER_BVC("protocols"), NM_protocol },
00412        { BER_BVC("rpc"), NM_rpc },
00413        { BER_BVC("services"), NM_service },
00414        { BER_BVC("shadow"), NM_shadow },
00415        { BER_BVNULL, 0 }
00416 };
00417 
00418 static slap_verbmasks pam_opts[] = {
00419        { BER_BVC("userhost"), NI_PAM_USERHOST },
00420        { BER_BVC("userservice"), NI_PAM_USERSVC },
00421        { BER_BVC("usergroup"), NI_PAM_USERGRP },
00422        { BER_BVC("hostservice"), NI_PAM_HOSTSVC },
00423        { BER_BVC("authz2dn"), NI_PAM_SASL2DN },
00424        { BER_BVC("uid2dn"), NI_PAM_UID2DN },
00425        { BER_BVNULL, 0 }
00426 };
00427 
00428 enum {
00429        NSS_SSD=1,
00430        NSS_MAP,
00431        NSS_PAM,
00432        NSS_PAMGROUP,
00433        NSS_PAMSESS
00434 };
00435 
00436 static ConfigDriver nss_cf_gen;
00437 
00438 static ConfigTable nsscfg[] = {
00439        { "nssov-ssd", "service> <url", 3, 3, 0, ARG_MAGIC|NSS_SSD,
00440               nss_cf_gen, "(OLcfgCtAt:3.1 NAME 'olcNssSsd' "
00441                      "DESC 'URL for searches in a given service' "
00442                      "EQUALITY caseIgnoreMatch "
00443                      "SYNTAX OMsDirectoryString )", NULL, NULL },
00444        { "nssov-map", "service> <orig> <new", 4, 4, 0, ARG_MAGIC|NSS_MAP,
00445               nss_cf_gen, "(OLcfgCtAt:3.2 NAME 'olcNssMap' "
00446                      "DESC 'Map <service> lookups of <orig> attr to <new> attr' "
00447                      "EQUALITY caseIgnoreMatch "
00448                      "SYNTAX OMsDirectoryString )", NULL, NULL },
00449        { "nssov-pam", "options", 2, 0, 0, ARG_MAGIC|NSS_PAM,
00450               nss_cf_gen, "(OLcfgCtAt:3.3 NAME 'olcNssPam' "
00451                      "DESC 'PAM authentication and authorization options' "
00452                      "EQUALITY caseIgnoreMatch "
00453                      "SYNTAX OMsDirectoryString )", NULL, NULL },
00454        { "nssov-pam-defhost", "hostname", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
00455               (void *)offsetof(struct nssov_info, ni_pam_defhost),
00456               "(OLcfgCtAt:3.4 NAME 'olcNssPamDefHost' "
00457                      "DESC 'Default hostname for service checks' "
00458                      "EQUALITY caseIgnoreMatch "
00459                      "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
00460        { "nssov-pam-group-dn", "DN", 2, 2, 0, ARG_MAGIC|ARG_DN|NSS_PAMGROUP,
00461               nss_cf_gen, "(OLcfgCtAt:3.5 NAME 'olcNssPamGroupDN' "
00462                      "DESC 'DN of group in which membership is required' "
00463                      "EQUALITY distinguishedNameMatch "
00464                      "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
00465        { "nssov-pam-group-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
00466               (void *)offsetof(struct nssov_info, ni_pam_group_ad),
00467               "(OLcfgCtAt:3.6 NAME 'olcNssPamGroupAD' "
00468                      "DESC 'Member attribute to use for group check' "
00469                      "EQUALITY caseIgnoreMatch "
00470                      "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
00471        { "nssov-pam-min-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
00472               (void *)offsetof(struct nssov_info, ni_pam_min_uid),
00473               "(OLcfgCtAt:3.7 NAME 'olcNssPamMinUid' "
00474                      "DESC 'Minimum UID allowed to login' "
00475                      "EQUALITY integerMatch "
00476                      "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
00477        { "nssov-pam-max-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
00478               (void *)offsetof(struct nssov_info, ni_pam_max_uid),
00479               "(OLcfgCtAt:3.8 NAME 'olcNssPamMaxUid' "
00480                      "DESC 'Maximum UID allowed to login' "
00481                      "EQUALITY integerMatch "
00482                      "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
00483        { "nssov-pam-template-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
00484               (void *)offsetof(struct nssov_info, ni_pam_template_ad),
00485               "(OLcfgCtAt:3.9 NAME 'olcNssPamTemplateAD' "
00486                      "DESC 'Attribute to use for template login name' "
00487                      "EQUALITY caseIgnoreMatch "
00488                      "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
00489        { "nssov-pam-template", "name", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
00490               (void *)offsetof(struct nssov_info, ni_pam_template),
00491               "(OLcfgCtAt:3.10 NAME 'olcNssPamTemplate' "
00492                      "DESC 'Default template login name' "
00493                      "EQUALITY caseIgnoreMatch "
00494                      "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
00495        { "nssov-pam-session", "service", 2, 2, 0, ARG_MAGIC|ARG_BERVAL|NSS_PAMSESS,
00496               nss_cf_gen, "(OLcfgCtAt:3.11 NAME 'olcNssPamSession' "
00497                      "DESC 'Services for which sessions will be recorded' "
00498                      "EQUALITY caseIgnoreMatch "
00499                      "SYNTAX OMsDirectoryString )", NULL, NULL },
00500        { NULL, NULL, 0,0,0, ARG_IGNORED }
00501 };
00502 
00503 static ConfigOCs nssocs[] = {
00504        { "( OLcfgCtOc:3.1 "
00505               "NAME 'olcNssOvConfig' "
00506               "DESC 'NSS lookup configuration' "
00507               "SUP olcOverlayConfig "
00508               "MAY ( olcNssSsd $ olcNssMap $ olcNssPam $ olcNssPamDefHost $ "
00509                      "olcNssPamGroupDN $ olcNssPamGroupAD $ "
00510                      "olcNssPamMinUid $ olcNssPamMaxUid $ olcNssPamSession $ "
00511                      "olcNssPamTemplateAD $ olcNssPamTemplate ) )",
00512               Cft_Overlay, nsscfg },
00513        { NULL, 0, NULL }
00514 };
00515 
00516 static int
00517 nss_cf_gen(ConfigArgs *c)
00518 {
00519        slap_overinst *on = (slap_overinst *)c->bi;
00520        nssov_info *ni = on->on_bi.bi_private;
00521        nssov_mapinfo *mi;
00522        int i, j, rc = 0;
00523        slap_mask_t m;
00524 
00525        if ( c->op == SLAP_CONFIG_EMIT ) {
00526               switch(c->type) {
00527               case NSS_SSD:
00528                      rc = 1;
00529                      for (i=NM_alias;i<NM_NONE;i++) {
00530                             struct berval scope;
00531                             struct berval ssd;
00532                             struct berval base;
00533 
00534                             mi = &ni->ni_maps[i];
00535 
00536                             /* ignore all-default services */
00537                             if ( mi->mi_scope == LDAP_SCOPE_DEFAULT &&
00538                                    bvmatch( &mi->mi_filter, &mi->mi_filter0 ) &&
00539                                    BER_BVISNULL( &mi->mi_base ))
00540                                    continue;
00541 
00542                             if ( BER_BVISNULL( &mi->mi_base ))
00543                                    base = ni->ni_db->be_nsuffix[0];
00544                             else
00545                                    base = mi->mi_base;
00546                             ldap_pvt_scope2bv(mi->mi_scope == LDAP_SCOPE_DEFAULT ?
00547                                    LDAP_SCOPE_SUBTREE : mi->mi_scope, &scope);
00548                             ssd.bv_len = STRLENOF(" ldap:///???") + nss_svcs[i].word.bv_len +
00549                                    base.bv_len + scope.bv_len + mi->mi_filter.bv_len;
00550                             ssd.bv_val = ch_malloc( ssd.bv_len + 1 );
00551                             sprintf(ssd.bv_val, "%s ldap:///%s??%s?%s", nss_svcs[i].word.bv_val,
00552                                    base.bv_val, scope.bv_val, mi->mi_filter.bv_val );
00553                             ber_bvarray_add( &c->rvalue_vals, &ssd );
00554                             rc = 0;
00555                      }
00556                      break;
00557               case NSS_MAP:
00558                      rc = 1;
00559                      for (i=NM_alias;i<NM_NONE;i++) {
00560 
00561                             mi = &ni->ni_maps[i];
00562                             for (j=0;!BER_BVISNULL(&mi->mi_attrkeys[j]);j++) {
00563                                    if ( ber_bvstrcasecmp(&mi->mi_attrkeys[j],
00564                                           &mi->mi_attrs[j].an_name)) {
00565                                           struct berval map;
00566 
00567                                           map.bv_len = nss_svcs[i].word.bv_len +
00568                                                  mi->mi_attrkeys[j].bv_len +
00569                                                  mi->mi_attrs[j].an_desc->ad_cname.bv_len + 2;
00570                                           map.bv_val = ch_malloc(map.bv_len + 1);
00571                                           sprintf(map.bv_val, "%s %s %s", nss_svcs[i].word.bv_val,
00572                                                  mi->mi_attrkeys[j].bv_val, mi->mi_attrs[j].an_desc->ad_cname.bv_val );
00573                                           ber_bvarray_add( &c->rvalue_vals, &map );
00574                                           rc = 0;
00575                                    }
00576                             }
00577                      }
00578                      break;
00579               case NSS_PAM:
00580                      rc = mask_to_verbs( pam_opts, ni->ni_pam_opts, &c->rvalue_vals );
00581                      break;
00582               case NSS_PAMGROUP:
00583                      if (!BER_BVISEMPTY( &ni->ni_pam_group_dn )) {
00584                             value_add_one( &c->rvalue_vals, &ni->ni_pam_group_dn );
00585                             value_add_one( &c->rvalue_nvals, &ni->ni_pam_group_dn );
00586                      } else {
00587                             rc = 1;
00588                      }
00589                      break;
00590               case NSS_PAMSESS:
00591                      if (ni->ni_pam_sessions) {
00592                             ber_bvarray_dup_x( &c->rvalue_vals, ni->ni_pam_sessions, NULL );
00593                      } else {
00594                             rc = 1;
00595                      }
00596                      break;
00597               }
00598               return rc;
00599        } else if ( c->op == LDAP_MOD_DELETE ) {
00600               /* FIXME */
00601               return 1;
00602        }
00603        switch( c->type ) {
00604        case NSS_SSD: {
00605               LDAPURLDesc *lud;
00606 
00607               i = verb_to_mask(c->argv[1], nss_svcs);
00608               if ( i == NM_NONE )
00609                      return 1;
00610 
00611               mi = &ni->ni_maps[i];
00612               rc = ldap_url_parse(c->argv[2], &lud);
00613               if ( rc )
00614                      return 1;
00615               do {
00616                      struct berval base;
00617                      /* Must be LDAP scheme */
00618                      if (strcasecmp(lud->lud_scheme,"ldap")) {
00619                             rc = 1;
00620                             break;
00621                      }
00622                      /* Host part, attrs, and extensions must be empty */
00623                      if (( lud->lud_host && *lud->lud_host ) ||
00624                             lud->lud_attrs || lud->lud_exts ) {
00625                             rc = 1;
00626                             break;
00627                      }
00628                      ber_str2bv( lud->lud_dn,0,0,&base);
00629                      rc = dnNormalize( 0,NULL,NULL,&base,&mi->mi_base,NULL);
00630                      if ( rc )
00631                             break;
00632                      if ( lud->lud_filter ) {
00633                             /* steal this */
00634                             ber_str2bv( lud->lud_filter,0,0,&mi->mi_filter);
00635                             lud->lud_filter = NULL;
00636                      }
00637                      mi->mi_scope = lud->lud_scope;
00638               } while(0);
00639               ldap_free_urldesc( lud );
00640               }
00641               break;
00642        case NSS_MAP:
00643               i = verb_to_mask(c->argv[1], nss_svcs);
00644               if ( i == NM_NONE )
00645                      return 1;
00646               rc = 1;
00647               mi = &ni->ni_maps[i];
00648               for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
00649                      if (!strcasecmp(c->argv[2],mi->mi_attrkeys[j].bv_val)) {
00650                             AttributeDescription *ad = NULL;
00651                             const char *text;
00652                             rc = slap_str2ad( c->argv[3], &ad, &text);
00653                             if ( rc == 0 ) {
00654                                    mi->mi_attrs[j].an_desc = ad;
00655                                    mi->mi_attrs[j].an_name = ad->ad_cname;
00656                             }
00657                             break;
00658                      }
00659               }
00660               break;
00661        case NSS_PAM:
00662               m = ni->ni_pam_opts;
00663               i = verbs_to_mask(c->argc, c->argv, pam_opts, &m);
00664               if (i == 0) {
00665                      ni->ni_pam_opts = m;
00666                      if ((m & NI_PAM_USERHOST) && !nssov_pam_host_ad) {
00667                             const char *text;
00668                             i = slap_str2ad("host", &nssov_pam_host_ad, &text);
00669                             if (i != LDAP_SUCCESS) {
00670                                    snprintf(c->cr_msg, sizeof(c->cr_msg),
00671                                           "nssov: host attr unknown: %s", text);
00672                                    Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg,0,0);
00673                                    rc = 1;
00674                                    break;
00675                             }
00676                      }
00677                      if ((m & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) && !nssov_pam_svc_ad) {
00678                             const char *text;
00679                             i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
00680                             if (i != LDAP_SUCCESS) {
00681                                    snprintf(c->cr_msg, sizeof(c->cr_msg),
00682                                           "nssov: authorizedService attr unknown: %s", text);
00683                                    Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg,0,0);
00684                                    rc = 1;
00685                                    break;
00686                             }
00687                      }
00688               } else {
00689                      rc = 1;
00690               }
00691               break;
00692        case NSS_PAMGROUP:
00693               ni->ni_pam_group_dn = c->value_ndn;
00694               ch_free( c->value_dn.bv_val );
00695               break;
00696        case NSS_PAMSESS:
00697               ber_bvarray_add( &ni->ni_pam_sessions, &c->value_bv );
00698               break;
00699        }
00700        return rc;
00701 }
00702 
00703 static int
00704 nssov_db_init(
00705        BackendDB *be,
00706        ConfigReply *cr )
00707 {
00708        slap_overinst *on = (slap_overinst *)be->bd_info;
00709        nssov_info *ni;
00710        nssov_mapinfo *mi;
00711        int rc;
00712 
00713        rc = nssov_pam_init();
00714        if (rc) return rc;
00715 
00716        ni = ch_calloc( 1, sizeof(nssov_info) );
00717        on->on_bi.bi_private = ni;
00718 
00719        /* set up map keys */
00720        nssov_alias_init(ni);
00721        nssov_ether_init(ni);
00722        nssov_group_init(ni);
00723        nssov_host_init(ni);
00724        nssov_netgroup_init(ni);
00725        nssov_network_init(ni);
00726        nssov_passwd_init(ni);
00727        nssov_protocol_init(ni);
00728        nssov_rpc_init(ni);
00729        nssov_service_init(ni);
00730        nssov_shadow_init(ni);
00731 
00732        ni->ni_db = be->bd_self;
00733        ni->ni_pam_opts = NI_PAM_UID2DN;
00734 
00735        return 0;
00736 }
00737 
00738 static int
00739 nssov_db_destroy(
00740        BackendDB *be,
00741        ConfigReply *cr )
00742 {
00743 }
00744 
00745 static int
00746 nssov_db_open(
00747        BackendDB *be,
00748        ConfigReply *cr )
00749 {
00750        slap_overinst *on = (slap_overinst *)be->bd_info;
00751        nssov_info *ni = on->on_bi.bi_private;
00752        nssov_mapinfo *mi;
00753 
00754        int i, sock;
00755        struct sockaddr_un addr;
00756 
00757        /* Set default bases */
00758        for (i=0; i<NM_NONE; i++) {
00759               if ( BER_BVISNULL( &ni->ni_maps[i].mi_base )) {
00760                      ber_dupbv( &ni->ni_maps[i].mi_base, &be->be_nsuffix[0] );
00761               }
00762               if ( ni->ni_maps[i].mi_scope == LDAP_SCOPE_DEFAULT )
00763                      ni->ni_maps[i].mi_scope = LDAP_SCOPE_SUBTREE;
00764        }
00765        /* validate attribute maps */
00766        mi = ni->ni_maps;
00767        for ( i=0; i<NM_NONE; i++,mi++) {
00768               const char *text;
00769               int j;
00770               for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
00771                      /* skip attrs we already validated */
00772                      if ( mi->mi_attrs[j].an_desc ) continue;
00773                      if ( slap_bv2ad( &mi->mi_attrs[j].an_name,
00774                             &mi->mi_attrs[j].an_desc, &text )) {
00775                             Debug(LDAP_DEBUG_ANY,"nssov: invalid attr \"%s\": %s\n",
00776                                    mi->mi_attrs[j].an_name.bv_val, text, 0 );
00777                             return -1;
00778                      }
00779               }
00780               BER_BVZERO(&mi->mi_attrs[j].an_name);
00781               mi->mi_attrs[j].an_desc = NULL;
00782        }
00783 
00784        /* Find host and authorizedService definitions */
00785        if ((ni->ni_pam_opts & NI_PAM_USERHOST) && !nssov_pam_host_ad)
00786        {
00787               const char *text;
00788               i = slap_str2ad("host", &nssov_pam_host_ad, &text);
00789               if (i != LDAP_SUCCESS) {
00790                      Debug(LDAP_DEBUG_ANY,"nssov: host attr unknown: %s\n",
00791                             text, 0, 0 );
00792                      return -1;
00793               }
00794        }
00795        if ((ni->ni_pam_opts & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) &&
00796               !nssov_pam_svc_ad)
00797        {
00798               const char *text;
00799               i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
00800               if (i != LDAP_SUCCESS) {
00801                      Debug(LDAP_DEBUG_ANY,"nssov: authorizedService attr unknown: %s\n",
00802                             text, 0, 0 );
00803                      return -1;
00804               }
00805        }
00806        if ( slapMode & SLAP_SERVER_MODE ) {
00807               /* make sure /var/run/nslcd exists */
00808               if (mkdir(NSLCD_PATH, (mode_t) 0555)) {
00809                      Debug(LDAP_DEBUG_TRACE,"nssov: mkdir(%s) failed (ignored): %s\n",
00810                                    NSLCD_PATH,strerror(errno),0);
00811               } else {
00812                      Debug(LDAP_DEBUG_TRACE,"nssov: created %s\n",NSLCD_PATH,0,0);
00813               }
00814 
00815               /* create a socket */
00816               if ( (sock=socket(PF_UNIX,SOCK_STREAM,0))<0 )
00817               {
00818                      Debug(LDAP_DEBUG_ANY,"nssov: cannot create socket: %s\n",strerror(errno),0,0);
00819                      return -1;
00820               }
00821               /* remove existing named socket */
00822               if (unlink(NSLCD_SOCKET)<0)
00823               {
00824                      Debug( LDAP_DEBUG_TRACE,"nssov: unlink() of "NSLCD_SOCKET" failed (ignored): %s\n",
00825                                                  strerror(errno),0,0);
00826               }
00827               /* create socket address structure */
00828               memset(&addr,0,sizeof(struct sockaddr_un));
00829               addr.sun_family=AF_UNIX;
00830               strncpy(addr.sun_path,NSLCD_SOCKET,sizeof(addr.sun_path));
00831               addr.sun_path[sizeof(addr.sun_path)-1]='\0';
00832               /* bind to the named socket */
00833               if (bind(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr_un)))
00834               {
00835                      Debug( LDAP_DEBUG_ANY,"nssov: bind() to "NSLCD_SOCKET" failed: %s",
00836                                                  strerror(errno),0,0);
00837                      if (close(sock))
00838                             Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
00839                      return -1;
00840               }
00841               /* close the file descriptor on exit */
00842               if (fcntl(sock,F_SETFD,FD_CLOEXEC)<0)
00843               {
00844                      Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,O_NONBLOCK) failed: %s",strerror(errno),0,0);
00845                      if (close(sock))
00846                             Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
00847                      return -1;
00848               }
00849               /* set permissions of socket so anybody can do requests */
00850               /* Note: we use chmod() here instead of fchmod() because
00851                       fchmod does not work on sockets
00852                       http://www.opengroup.org/onlinepubs/009695399/functions/fchmod.html
00853                       http://lkml.org/lkml/2005/5/16/11 */
00854               if (chmod(NSLCD_SOCKET,(mode_t)0666))
00855               {
00856                      Debug( LDAP_DEBUG_ANY,"nssov: chmod(0666) failed: %s",strerror(errno),0,0);
00857                      if (close(sock))
00858                             Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
00859                      return -1;
00860               }
00861               /* start listening for connections */
00862               if (listen(sock,SOMAXCONN)<0)
00863               {
00864                      Debug( LDAP_DEBUG_ANY,"nssov: listen() failed: %s",strerror(errno),0,0);
00865                      if (close(sock))
00866                             Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
00867                      return -1;
00868               }
00869               ni->ni_socket = sock;
00870               ni->ni_conn = connection_client_setup( sock, acceptconn, ni );
00871        }
00872 
00873        return 0;
00874 }
00875 
00876 static int
00877 nssov_db_close(
00878        BackendDB *be,
00879        ConfigReply *cr )
00880 {
00881        slap_overinst *on = (slap_overinst *)be->bd_info;
00882        nssov_info *ni = on->on_bi.bi_private;
00883 
00884        if ( slapMode & SLAP_SERVER_MODE ) {
00885               /* close socket if it's still in use */
00886               if (ni->ni_socket >= 0);
00887               {
00888                      if (close(ni->ni_socket))
00889                             Debug( LDAP_DEBUG_ANY,"problem closing server socket (ignored): %s",strerror(errno),0,0);
00890                      ni->ni_socket = -1;
00891               }
00892               /* remove existing named socket */
00893               if (unlink(NSLCD_SOCKET)<0)
00894               {
00895                      Debug( LDAP_DEBUG_TRACE,"unlink() of "NSLCD_SOCKET" failed (ignored): %s",
00896                             strerror(errno),0,0);
00897               }
00898        }
00899 }
00900 
00901 static slap_overinst nssov;
00902 
00903 int
00904 nssov_initialize( void )
00905 {
00906        int rc;
00907 
00908        nssov.on_bi.bi_type = "nssov";
00909        nssov.on_bi.bi_db_init = nssov_db_init;
00910        nssov.on_bi.bi_db_destroy = nssov_db_destroy;
00911        nssov.on_bi.bi_db_open = nssov_db_open;
00912        nssov.on_bi.bi_db_close = nssov_db_close;
00913 
00914        nssov.on_bi.bi_cf_ocs = nssocs;
00915 
00916        rc = config_register_schema( nsscfg, nssocs );
00917        if ( rc ) return rc;
00918 
00919        return overlay_register(&nssov);
00920 }
00921 
00922 #if SLAPD_OVER_NSSOV == SLAPD_MOD_DYNAMIC
00923 int
00924 init_module( int argc, char *argv[] )
00925 {
00926        return nssov_initialize();
00927 }
00928 #endif