]> git.sur5r.net Git - openldap/commitdiff
Sync up nssov
authorQuanah Gibson-Mount <quanah@openldap.org>
Wed, 3 Jun 2009 22:46:54 +0000 (22:46 +0000)
committerQuanah Gibson-Mount <quanah@openldap.org>
Wed, 3 Jun 2009 22:46:54 +0000 (22:46 +0000)
23 files changed:
contrib/slapd-modules/nssov/Makefile
contrib/slapd-modules/nssov/README
contrib/slapd-modules/nssov/alias.c
contrib/slapd-modules/nssov/ether.c
contrib/slapd-modules/nssov/group.c
contrib/slapd-modules/nssov/host.c
contrib/slapd-modules/nssov/ldapns.schema [new file with mode: 0644]
contrib/slapd-modules/nssov/netgroup.c
contrib/slapd-modules/nssov/network.c
contrib/slapd-modules/nssov/nss-ldapd/nslcd.h
contrib/slapd-modules/nssov/nss-ldapd/nss/Makefile.am
contrib/slapd-modules/nssov/nss-ldapd/nss/Makefile.in
contrib/slapd-modules/nssov/nss-ldapd/nss/exports.linux
contrib/slapd-modules/nssov/nss-ldapd/nss/pam.c [new file with mode: 0644]
contrib/slapd-modules/nssov/nssov.c
contrib/slapd-modules/nssov/nssov.h
contrib/slapd-modules/nssov/pam.c [new file with mode: 0644]
contrib/slapd-modules/nssov/passwd.c
contrib/slapd-modules/nssov/protocol.c
contrib/slapd-modules/nssov/rpc.c
contrib/slapd-modules/nssov/service.c
contrib/slapd-modules/nssov/shadow.c
contrib/slapd-modules/nssov/slapo-nssov.5 [new file with mode: 0644]

index 1d231ef57d5317721a1d6477cd820906b24f8ba5..dd2199926286016533f90cb50e7e7cce0227f394 100644 (file)
@@ -32,7 +32,7 @@ all:  nssov.la
 XOBJS = tio.lo
 
 OBJS = alias.lo ether.lo group.lo host.lo netgroup.lo network.lo \
-       nssov.lo passwd.lo protocol.lo rpc.lo service.lo shadow.lo
+       nssov.lo passwd.lo protocol.lo rpc.lo service.lo shadow.lo pam.lo
 
 .SUFFIXES: .c .o .lo
 
index e51c90d4b35671329ec15ad96407d11f900dbe8d..37fcda0fa94cf157b888406f6185f4df5dc3496f 100644 (file)
@@ -1,4 +1,4 @@
-Copyright 2008 Howard Chu, Symas Corp. All rights reserved.
+Copyright 2008-2009 Howard Chu, Symas Corp. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted only as authorized by the OpenLDAP
@@ -11,7 +11,8 @@ top-level directory of the distribution or, alternatively, at
 This directory contains a slapd overlay, nssov, that handles
 NSS lookup requests through a local Unix Domain socket. It uses the
 same IPC protocol as Arthur de Jong's nss-ldapd, and a complete
-copy of the nss-ldapd source is included here.
+copy of the nss-ldapd source is included here. It also handles
+PAM requests.
 
 To use this code, you will need the client-side stub library from
 nss-ldapd (which resides in nss-ldapd/nss). You will not need the
@@ -38,7 +39,7 @@ use RFC2307bis.)
 The overlay may be configured with Service Search Descriptors (SSDs)
 for each NSS service that will be used. SSDs are configured using
 
-       nssov-svc <service> <url>
+       nssov-ssd <service> <url>
 
 where the <service> may be one of
        alias
@@ -75,8 +76,51 @@ of the config entry is
        objectClass: olcOverlayConfig
        objectClass: olcNssOvConfig
        olcOverlay: {0}nssov
-       olcNssSvc: passwd ldap:///ou=users,dc=example,dc=com??one
+       olcNssSsd: passwd ldap:///ou=users,dc=example,dc=com??one
        olcNssMap: passwd uid accountName
 
 which enables the passwd service, and uses the accountName attribute to
 fetch what is usually retrieved from the uid attribute.
+
+PAM authentication, account management, session management, and password
+management are supported.
+
+Authentication is performed using Simple Binds. Since all operations occur
+inside the slapd overlay, "fake" connections are used and they are
+inherently secure. Two methods of mapping the PAM username to an LDAP DN
+are provided:
+  the mapping can be accomplished using slapd's authz-regexp facility. In
+this case, a DN of the form
+       cn=<service>+uid=<user>,cn=<hostname>,cn=pam,cn=auth
+is fed into the regexp matcher. If a match is produced, the resulting DN
+is used.
+  otherwise, the NSS passwd map is invoked (which means it must already
+be configured).
+
+If no DN is found, the overlay returns PAM_USER_UNKNOWN. If the DN is
+found, and Password Policy is supported, then the Bind will use the
+Password Policy control and return expiration information to PAM.
+
+Account management also uses two methods. These methods depend on the
+ldapns.schema included with the nssov source.
+  The first is identical to the method used in PADL's pam_ldap module:
+host and authorizedService attributes may be looked up in the user's entry,
+and checked to determine access. Also a check may be performed to see if
+the user is a member of a particular group. This method is pretty
+inflexible and doesn't scale well to large networks of users, hosts,
+and services.
+  The second uses slapd's ACL engine to check if the user has "compare"
+privilege on an ipHost object whose name matches the current hostname, and
+whose authorizedService attribute matches the current service name. This
+method is preferred, since it allows authorization to be centralized in
+the ipHost entries instead of scattered across the entire user population.
+The ipHost entries must have an authorizedService attribute (e.g. by way
+of the authorizedServiceObject auxiliary class) to use this method.
+
+Session management: the overlay may optionally add a "logged in" attribute
+to a user's entry for successful logins, and delete the corresponding
+value upon logout. The attribute value is of the form
+       <generalizedTime> <host> <service> <tty> (<ruser@rhost>)
+
+Password management: the overlay will perform a PasswordModify exop
+in the server for the given user.
index 7ff83034baa3e92810e8d66820eb817d67be187b..35aaa8f71d748652ef04a398c627aa0bb667558c 100644 (file)
@@ -58,7 +58,7 @@ static int write_alias(nssov_alias_cbp *cbp,Entry *entry)
                a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[0].an_desc);
                if ( !a )
                {
-                       Debug(LDAP_DEBUG_ANY,"alias entry %s does not contain %s value",
+                       Debug(LDAP_DEBUG_ANY,"alias entry %s does not contain %s value\n",
                                entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val,0 );
                        return 0;
                }
@@ -98,7 +98,7 @@ NSSOV_HANDLE(
        READ_STRING_BUF2(fp,cbp.buf,sizeof(cbp.buf));
        cbp.name.bv_len = tmpint32;
        cbp.name.bv_val = cbp.buf;,
-       Debug(LDAP_DEBUG_TRACE,"nssov_alias_byname(%s)",cbp.name.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_alias_byname(%s)\n",cbp.name.bv_val,0,0);,
        NSLCD_ACTION_ALIAS_BYNAME,
        nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
 )
@@ -108,7 +108,7 @@ NSSOV_HANDLE(
        struct berval filter;
        /* no parameters to read */
        BER_BVZERO(&cbp.name);,
-       Debug(LDAP_DEBUG,"nssov_alias_all()",0,0,0);,
+       Debug(LDAP_DEBUG,"nssov_alias_all()\n",0,0,0);,
        NSLCD_ACTION_ALIAS_ALL,
        (filter=cbp.mi->mi_filter,0)
 )
index 3055a2aaa03c47cd00d4c34d96737c96f04d54e7..9f07bbedd24fa9a6037f09300446aefbce6ed2f9 100644 (file)
@@ -74,7 +74,7 @@ static int write_ether(nssov_ether_cbp *cbp,Entry *entry)
                a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[0].an_desc);
                if ( !a )
                {
-                       Debug(LDAP_DEBUG_ANY,"ether entry %s does not contain %s value",
+                       Debug(LDAP_DEBUG_ANY,"ether entry %s does not contain %s value\n",
                                                        entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val,0 );
                        return 0;
                }
@@ -92,7 +92,7 @@ static int write_ether(nssov_ether_cbp *cbp,Entry *entry)
                a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[1].an_desc);
                if ( !a )
                {
-                       Debug(LDAP_DEBUG_ANY,"ether entry %s does not contain %s value",
+                       Debug(LDAP_DEBUG_ANY,"ether entry %s does not contain %s value\n",
                                                        entry->e_name.bv_val,cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val,0 );
                        return 0;
                }
@@ -127,7 +127,7 @@ NSSOV_HANDLE(
        READ_STRING_BUF2(fp,cbp.buf,sizeof(cbp.buf));
        cbp.name.bv_len = tmpint32;
        cbp.name.bv_val = cbp.buf;,
-       Debug(LDAP_DEBUG_TRACE,"nssov_ether_byname(%s)",cbp.name.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_ether_byname(%s)\n",cbp.name.bv_val,0,0);,
        NSLCD_ACTION_ETHER_BYNAME,
        nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
 )
@@ -148,7 +148,7 @@ NSSOV_HANDLE(
                addr.ether_addr_octet[4],
                addr.ether_addr_octet[5]);
        cbp.addr.bv_val = cbp.buf;,
-       Debug(LDAP_DEBUG_TRACE,"nssov_ether_byether(%s)",cbp.addr.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_ether_byether(%s)\n",cbp.addr.bv_val,0,0);,
        NSLCD_ACTION_ETHER_BYETHER,
        nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter)
 )
@@ -159,7 +159,7 @@ NSSOV_HANDLE(
        /* no parameters to read */
        BER_BVZERO(&cbp.name);
        BER_BVZERO(&cbp.addr);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_ether_all()",0,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_ether_all()\n",0,0,0);,
        NSLCD_ACTION_ETHER_ALL,
        (filter=cbp.mi->mi_filter,0)
 )
index 4c13acedbadd8bdccd4143cd31745564c1252c00..866dcd6a108fb00da0655917a345bfb73f8c501b 100644 (file)
@@ -1,7 +1,7 @@
 /* group.c - group lookup routines */
 /* $OpenLDAP$ */
 /*
- * Copyright 2008 by Howard Chu, Symas Corp.
+ * Copyright 2008-2009 by Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -121,6 +121,10 @@ static int isvalidgroupname(struct berval *name)
        /* check other characters */
        for (i=1;i<name->bv_len;i++)
        {
+#ifndef STRICT_GROUPS
+               /* allow spaces too */
+               if (name->bv_val[i] == ' ') continue;
+#endif
                if ( ! ( (name->bv_val[i]>='A' && name->bv_val[i] <= 'Z') ||
                                                 (name->bv_val[i]>='a' && name->bv_val[i] <= 'z') ||
                                                 (name->bv_val[i]>='0' && name->bv_val[i] <= '9') ||
@@ -145,7 +149,7 @@ static int write_group(nssov_group_cbp *cbp,Entry *entry)
                a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
                if ( !a )
                {
-                       Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value",
+                       Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n",
                                        entry->e_name.bv_val, cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val,0);
                        return 0;
                }
@@ -163,7 +167,7 @@ static int write_group(nssov_group_cbp *cbp,Entry *entry)
                a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GID_KEY].an_desc);
                if ( !a )
                {
-                       Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value",
+                       Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n",
                                        entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val,0);
                        return 0;
                }
@@ -225,7 +229,7 @@ static int write_group(nssov_group_cbp *cbp,Entry *entry)
        {
                if (!isvalidgroupname(&names[i]))
                {
-                       Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains invalid group name: \"%s\"",
+                       Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains invalid group name: \"%s\"\n",
                                                                                                        entry->e_name.bv_val,names[i].bv_val,0);
                }
                else
@@ -237,7 +241,7 @@ static int write_group(nssov_group_cbp *cbp,Entry *entry)
                                gid_t gid;
                                gid = strtol(gids[j].bv_val, &tmp, 0);
                                if ( *tmp ) {
-                                       Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains non-numeric %s value: \"%s\"",
+                                       Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains non-numeric %s value: \"%s\"\n",
                                                entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val,
                                                names[i].bv_val);
                                        continue;
@@ -275,14 +279,14 @@ NSSOV_HANDLE(
        cbp.name.bv_len = tmpint32;
        cbp.name.bv_val = cbp.buf;
        if (!isvalidgroupname(&cbp.name)) {
-               Debug(LDAP_DEBUG_ANY,"nssov_group_byname(%s): invalid group name",cbp.name.bv_val,0,0);
+               Debug(LDAP_DEBUG_ANY,"nssov_group_byname(%s): invalid group name\n",cbp.name.bv_val,0,0);
                return -1;
        }
        cbp.wantmembers = 1;
        cbp.ni = ni;
        BER_BVZERO(&cbp.gidnum);
        BER_BVZERO(&cbp.user);,
-       Debug(LDAP_DEBUG_TRACE,"nslcd_group_byname(%s)",cbp.name.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nslcd_group_byname(%s)\n",cbp.name.bv_val,0,0);,
        NSLCD_ACTION_GROUP_BYNAME,
        nssov_filter_byname(cbp.mi,CN_KEY,&cbp.name,&filter)
 )
@@ -300,7 +304,7 @@ NSSOV_HANDLE(
        cbp.ni = ni;
        BER_BVZERO(&cbp.name);
        BER_BVZERO(&cbp.user);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_group_bygid(%s)",cbp.gidnum.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_group_bygid(%s)\n",cbp.gidnum.bv_val,0,0);,
        NSLCD_ACTION_GROUP_BYGID,
        nssov_filter_byid(cbp.mi,GID_KEY,&cbp.gidnum,&filter)
 )
@@ -314,14 +318,14 @@ NSSOV_HANDLE(
        cbp.user.bv_len = tmpint32;
        cbp.user.bv_val = cbp.buf;
        if (!isvalidusername(&cbp.user)) {
-               Debug(LDAP_DEBUG_ANY,"nssov_group_bymember(%s): invalid user name",cbp.user.bv_val,0,0);
+               Debug(LDAP_DEBUG_ANY,"nssov_group_bymember(%s): invalid user name\n",cbp.user.bv_val,0,0);
                return -1;
        }
        cbp.wantmembers = 0;
        cbp.ni = ni;
        BER_BVZERO(&cbp.name);
        BER_BVZERO(&cbp.gidnum);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_group_bymember(%s)",cbp.user.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_group_bymember(%s)\n",cbp.user.bv_val,0,0);,
        NSLCD_ACTION_GROUP_BYMEMBER,
        mkfilter_group_bymember(&cbp,&filter)
 )
@@ -334,7 +338,7 @@ NSSOV_HANDLE(
        cbp.ni = ni;
        BER_BVZERO(&cbp.name);
        BER_BVZERO(&cbp.gidnum);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_group_all()",0,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_group_all()\n",0,0,0);,
        NSLCD_ACTION_GROUP_ALL,
        (filter=cbp.mi->mi_filter,0)
 )
index 7ccf8247bba6321c2a28a6eaecadfec96d67d458..588e28e6e6c9030ef8c0b2574184194994ffebdf 100644 (file)
@@ -59,7 +59,7 @@ static int write_host(nssov_host_cbp *cbp,Entry *entry)
        a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
        if ( !a || !a->a_vals )
        {
-               Debug(LDAP_DEBUG_ANY,"host entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"host entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val, 0 );
                return 0;
        }
@@ -82,7 +82,7 @@ static int write_host(nssov_host_cbp *cbp,Entry *entry)
        a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
        if ( !a || !a->a_vals )
        {
-               Debug(LDAP_DEBUG_ANY,"host entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"host entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
                return 0;
        }
@@ -119,7 +119,7 @@ NSSOV_HANDLE(
        READ_STRING_BUF2(fp,cbp.buf,sizeof(cbp.buf));
        cbp.name.bv_len = tmpint32;
        cbp.name.bv_val = cbp.buf;,
-       Debug(LDAP_DEBUG_TRACE,"nssov_host_byname(%s)",cbp.name.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_host_byname(%s)\n",cbp.name.bv_val,0,0);,
        NSLCD_ACTION_HOST_BYNAME,
        nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
 )
@@ -137,12 +137,12 @@ NSSOV_HANDLE(
        /* translate the address to a string */
        if (inet_ntop(af,addr,cbp.buf,sizeof(cbp.buf))==NULL)
        {
-               Debug(LDAP_DEBUG_ANY,"nssov: unable to convert address to string",0,0,0);
+               Debug(LDAP_DEBUG_ANY,"nssov: unable to convert address to string\n",0,0,0);
                return -1;
        }
        cbp.addr.bv_val = cbp.buf;
        cbp.addr.bv_len = strlen(cbp.buf);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_host_byaddr(%s)",cbp.addr.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_host_byaddr(%s)\n",cbp.addr.bv_val,0,0);,
        NSLCD_ACTION_HOST_BYADDR,
        nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter)
 )
@@ -153,7 +153,7 @@ NSSOV_HANDLE(
        /* no parameters to read */
        BER_BVZERO(&cbp.name);
        BER_BVZERO(&cbp.addr);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_host_all()",0,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_host_all()\n",0,0,0);,
        NSLCD_ACTION_HOST_ALL,
        (filter=cbp.mi->mi_filter,0)
 )
diff --git a/contrib/slapd-modules/nssov/ldapns.schema b/contrib/slapd-modules/nssov/ldapns.schema
new file mode 100644 (file)
index 0000000..2cf9c0a
--- /dev/null
@@ -0,0 +1,25 @@
+# $OpenLDAP$
+# $Id: ldapns.schema,v 1.3 2003/05/29 12:57:29 lukeh Exp $
+# LDAP Name Service Additional Schema
+# http://www.iana.org/assignments/gssapi-service-names
+
+#
+# Not part of the distribution: this is a workaround!
+#
+
+attributetype ( 1.3.6.1.4.1.5322.17.2.1 NAME 'authorizedService'
+          DESC 'IANA GSS-API authorized service name'
+          EQUALITY caseIgnoreMatch
+          SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+objectclass ( 1.3.6.1.4.1.5322.17.1.1 NAME 'authorizedServiceObject'
+          DESC 'Auxiliary object class for adding authorizedService attribute'
+          SUP top
+          AUXILIARY
+          MAY authorizedService )
+
+objectclass ( 1.3.6.1.4.1.5322.17.1.2 NAME 'hostObject'
+          DESC 'Auxiliary object class for adding host attribute'
+          SUP top
+          AUXILIARY
+          MAY host )
index 7fe781e37a87495f29372561794e15b150b4445f..b9d95c44c17c2a59c253a2ae95aec1029c338ec7 100644 (file)
@@ -91,7 +91,7 @@ static int write_netgroup_triple(TFILE *fp,const char *triple)
        /* we should have a bracket now */
        if (triple[i]!='(')
        {
-               Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): entry does not begin with '(' (entry skipped)",0,0,0);
+               Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): entry does not begin with '(' (entry skipped)\n",0,0,0);
                return 0;
        }
        i++;
@@ -101,7 +101,7 @@ static int write_netgroup_triple(TFILE *fp,const char *triple)
                /* nothing else to do */ ;
        if (triple[i]!=',')
        {
-               Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ',' (entry skipped)",0,0,0);
+               Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ',' (entry skipped)\n",0,0,0);
                return 0;
        }
        hoste=i;
@@ -112,7 +112,7 @@ static int write_netgroup_triple(TFILE *fp,const char *triple)
                /* nothing else to do */ ;
        if (triple[i]!=',')
        {
-               Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ',' (entry skipped)",0,0,0);
+               Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ',' (entry skipped)\n",0,0,0);
                return 0;
        }
        usere=i;
@@ -123,7 +123,7 @@ static int write_netgroup_triple(TFILE *fp,const char *triple)
                /* nothing else to do */ ;
        if (triple[i]!=')')
        {
-               Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ')' (entry skipped)",0,0,0);
+               Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ')' (entry skipped)\n",0,0,0);
                return 0;
        }
        domaine=i;
@@ -134,7 +134,7 @@ static int write_netgroup_triple(TFILE *fp,const char *triple)
        /* if anything is left in the string we have a problem */
        if (triple[i]!='\0')
        {
-               Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): string contains trailing data (entry skipped)",0,0,0);
+               Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): string contains trailing data (entry skipped)\n",0,0,0);
                return 0;
        }
        /* write strings */
@@ -191,7 +191,7 @@ NSSOV_HANDLE(
        READ_STRING_BUF2(fp,cbp.buf,sizeof(cbp.buf));,
        cbp.name.bv_len = tmpint32;
        cbp.name.bv_val = cbp.buf;
-       Debug(LDAP_DEBUG_TRACE,"nssov_netgroup_byname(%s)",cbp.name.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_netgroup_byname(%s)\n",cbp.name.bv_val,0,0);,
        NSLCD_ACTION_NETGROUP_BYNAME,
        nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
 )
index 4a6260179fac95caa01bb134853e8461aa899957..efaf8b00cac9ed3828045ca52fe571fe2caf7b3b 100644 (file)
@@ -59,7 +59,7 @@ static int write_network(nssov_network_cbp *cbp,Entry *entry)
        a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
        if ( !a || !a->a_vals )
        {
-               Debug(LDAP_DEBUG_ANY,"network entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"network entry %s does not contain %s value\n",
                        entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val,0);
                return 0;
        }
@@ -82,7 +82,7 @@ static int write_network(nssov_network_cbp *cbp,Entry *entry)
        a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
        if ( !a || !a->a_vals )
        {
-               Debug(LDAP_DEBUG_ANY,"network entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"network entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
                return 0;
        }
@@ -119,7 +119,7 @@ NSSOV_HANDLE(
        READ_STRING_BUF2(fp,cbp.buf,sizeof(cbp.buf));
        cbp.name.bv_len = tmpint32;
        cbp.name.bv_val = cbp.buf;,
-       Debug(LDAP_DEBUG_TRACE,"nssov_network_byname(%s)",cbp.name.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_network_byname(%s)\n",cbp.name.bv_val,0,0);,
        NSLCD_ACTION_NETWORK_BYNAME,
        nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
 )
@@ -137,12 +137,12 @@ NSSOV_HANDLE(
        /* translate the address to a string */
        if (inet_ntop(af,addr,cbp.buf,sizeof(cbp.buf))==NULL)
        {
-               Debug(LDAP_DEBUG_ANY,"nssov: unable to convert address to string",0,0,0);
+               Debug(LDAP_DEBUG_ANY,"nssov: unable to convert address to string\n",0,0,0);
                return -1;
        }
        cbp.addr.bv_val = cbp.buf;
        cbp.addr.bv_len = strlen(cbp.buf);,
-       Debug(LDAP_DEBUG_TRACE,"nslcd_network_byaddr(%s)",cbp.addr.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nslcd_network_byaddr(%s)\n",cbp.addr.bv_val,0,0);,
        NSLCD_ACTION_NETWORK_BYADDR,
        nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter)
 )
@@ -153,7 +153,7 @@ NSSOV_HANDLE(
        /* no parameters to read */
        BER_BVZERO(&cbp.name);
        BER_BVZERO(&cbp.addr);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_network_all()",0,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_network_all()\n",0,0,0);,
        NSLCD_ACTION_NETWORK_ALL,
        (filter=cbp.mi->mi_filter,0)
 )
index 38b0962ffa0307dfaac3705aefd7bb5590e97c81..7dc94370f6d9d043545bac2ce937e39c8aa11231 100644 (file)
 #define NSLCD_ACTION_SHADOW_BYNAME      2001
 #define NSLCD_ACTION_SHADOW_ALL         2005
 
+#define NSLCD_ACTION_PAM_AUTHC         20001
+#define NSLCD_ACTION_PAM_AUTHZ         20002
+#define NSLCD_ACTION_PAM_SESS_O        20003
+#define NSLCD_ACTION_PAM_SESS_C        20004
+#define NSLCD_ACTION_PAM_PWMOD         20005
+
 /* Request result codes. */
 #define NSLCD_RESULT_END              3 /* key was not found */
 #define NSLCD_RESULT_SUCCESS               0 /* everything ok */
 
+/* Partial list of PAM result codes. */
+#define NSLCD_PAM_SUCCESS             0 /* everything ok */
+#define NSLCD_PAM_PERM_DENIED         6 /* Permission denied */
+#define NSLCD_PAM_AUTH_ERR            7 /* Authc failure */
+#define NSLCD_PAM_CRED_INSUFFICIENT   8 /* Cannot access authc data */
+#define NSLCD_PAM_AUTHINFO_UNAVAIL    9 /* Cannot retrieve authc info */
+#define NSLCD_PAM_USER_UNKNOWN       10 /* User not known */
+#define NSLCD_PAM_MAXTRIES           11 /* Retry limit reached */
+#define NSLCD_PAM_NEW_AUTHTOK_REQD   12 /* Password expired */
+#define NSLCD_PAM_ACCT_EXPIRED       13 /* Account expired */
+#define NSLCD_PAM_SESSION_ERR        14 /* Cannot make/remove session record */
+#define NSLCD_PAM_AUTHTOK_DISABLE_AGING 23 /* Password aging disabled */
+#define NSLCD_PAM_IGNORE             25 /* Ignore module */
+#define NSLCD_PAM_ABORT              26 /* Fatal error */
+
 #endif /* not _NSLCD_H */
index 0167d20dffff58ae821307ed5bbc5cb84c356b9b..36de9ed3abad6ed5a1d08f86601411a09a249856 100644 (file)
@@ -33,10 +33,10 @@ nss_ldap_so_SOURCES = common.c common.h prototypes.h \
                       ../compat/attrs.h \
                       aliases.c ethers.c group.c hosts.c netgroup.c \
                       networks.c passwd.c protocols.c rpc.c services.c \
-                      shadow.c
+                      shadow.c pam.c
 nss_ldap_so_LDFLAGS = -shared -Wl,-soname,$(NSS_LDAP_NSS_VERSIONED) \
                       -Wl,--version-script,\$(srcdir)/exports.linux
-nss_ldap_so_LDADD = @nss_ldap_so_LIBS@ ../common/libtio.a
+nss_ldap_so_LDADD = @nss_ldap_so_LIBS@ ../common/libtio.a -lpam
 
 EXTRA_DIST = exports.linux
 
index 6ffc23af64f009d540c95ba45ba28a0f93976c2d..0482c75e43c484c325971ba03c1859317647f934 100644 (file)
@@ -70,7 +70,7 @@ am_nss_ldap_so_OBJECTS = common.$(OBJEXT) aliases.$(OBJEXT) \
        ethers.$(OBJEXT) group.$(OBJEXT) hosts.$(OBJEXT) \
        netgroup.$(OBJEXT) networks.$(OBJEXT) passwd.$(OBJEXT) \
        protocols.$(OBJEXT) rpc.$(OBJEXT) services.$(OBJEXT) \
-       shadow.$(OBJEXT)
+       shadow.$(OBJEXT) pam.$(OBJEXT)
 nss_ldap_so_OBJECTS = $(am_nss_ldap_so_OBJECTS)
 nss_ldap_so_DEPENDENCIES = ../common/libtio.a
 nss_ldap_so_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
@@ -206,12 +206,12 @@ nss_ldap_so_SOURCES = common.c common.h prototypes.h \
                       ../compat/attrs.h \
                       aliases.c ethers.c group.c hosts.c netgroup.c \
                       networks.c passwd.c protocols.c rpc.c services.c \
-                      shadow.c
+                      shadow.c pam.c
 
 nss_ldap_so_LDFLAGS = -shared -Wl,-soname,$(NSS_LDAP_NSS_VERSIONED) \
                       -Wl,--version-script,\$(srcdir)/exports.linux
 
-nss_ldap_so_LDADD = @nss_ldap_so_LIBS@ ../common/libtio.a
+nss_ldap_so_LDADD = @nss_ldap_so_LIBS@ ../common/libtio.a -lpam
 EXTRA_DIST = exports.linux
 all: all-am
 
@@ -266,6 +266,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hosts.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netgroup.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/networks.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passwd.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocols.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc.Po@am__quote@
index 62c031399ff20bb941544b54a80af6a4f9e9ce70..c7f9b1e6e3babee5103df6fbcddcf6297f85b717 100644 (file)
@@ -78,6 +78,14 @@ EXPORTED {
     _nss_ldap_getspent_r;
     _nss_ldap_endspent;
 
+       # pam - pluggable auth
+       pam_sm_acct_mgmt;
+       pam_sm_authenticate;
+       pam_sm_chauthtok;
+       pam_sm_close_session;
+       pam_sm_open_session;
+       pam_sm_setcred;
+
   # everything else should not be exported
   local:
     *;
diff --git a/contrib/slapd-modules/nssov/nss-ldapd/nss/pam.c b/contrib/slapd-modules/nssov/nss-ldapd/nss/pam.c
new file mode 100644 (file)
index 0000000..abeae19
--- /dev/null
@@ -0,0 +1,720 @@
+/*
+   pam.c - pam module functions
+
+   Copyright (C) 2009 Howard Chu
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301 USA
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "prototypes.h"
+#include "common.h"
+#include "compat/attrs.h"
+
+#ifndef HAVE_PAM_PAM_MODULES_H
+#include <security/pam_modules.h>
+#else
+#include <pam/pam_modules.h>
+#endif
+
+#define        CONST_ARG       const
+
+#define IGNORE_UNKNOWN 1
+#define IGNORE_UNAVAIL 2
+
+#define        PLD_CTX "PAM_LDAPD_CTX"
+
+#define        NSS2PAM_RC(rc,ignore,ok)        \
+       switch(rc) { \
+       case NSS_STATUS_SUCCESS: \
+               rc = ok; break; \
+       case NSS_STATUS_UNAVAIL: \
+               rc = (ignore & IGNORE_UNAVAIL) ? PAM_IGNORE : PAM_AUTHINFO_UNAVAIL; \
+               break; \
+       case NSS_STATUS_NOTFOUND: \
+               rc = (ignore & IGNORE_UNKNOWN) ? PAM_IGNORE: PAM_USER_UNKNOWN; \
+               break; \
+       default: \
+               rc = PAM_SYSTEM_ERR; break; \
+       }
+
+typedef struct pld_ctx {
+       char *user;
+       char *dn;
+       char *tmpluser;
+       char *authzmsg;
+       char *oldpw;
+       int authok;
+       int authz;
+       int sessid;
+       char buf[1024];
+} pld_ctx;
+
+static int nslcd2pam_rc(int rc)
+{
+#define        map(i)  case NSLCD_##i : rc = i; break
+       switch(rc) {
+               map(PAM_SUCCESS);
+               map(PAM_PERM_DENIED);
+               map(PAM_AUTH_ERR);
+               map(PAM_CRED_INSUFFICIENT);
+               map(PAM_AUTHINFO_UNAVAIL);
+               map(PAM_USER_UNKNOWN);
+               map(PAM_MAXTRIES);
+               map(PAM_NEW_AUTHTOK_REQD);
+               map(PAM_ACCT_EXPIRED);
+               map(PAM_SESSION_ERR);
+               map(PAM_AUTHTOK_DISABLE_AGING);
+               map(PAM_IGNORE);
+               map(PAM_ABORT);
+       }
+       return rc;
+}
+
+static void pam_clr_ctx(
+       pld_ctx *ctx)
+{
+       if (ctx->user) {
+               free(ctx->user);
+               ctx->user = NULL;
+       }
+       if (ctx->oldpw) {
+               memset(ctx->oldpw,0,strlen(ctx->oldpw));
+               free(ctx->oldpw);
+               ctx->oldpw = NULL;
+       }
+       ctx->dn = NULL;
+       ctx->tmpluser = NULL;
+       ctx->authzmsg = NULL;
+       ctx->authok = 0;
+       ctx->authz = 0;
+}
+
+static void pam_del_ctx(
+       pam_handle_t *pamh, void *data, int err)
+{
+       pld_ctx *ctx = data;
+       pam_clr_ctx(ctx);
+       free(ctx);
+}
+
+static int pam_get_ctx(
+       pam_handle_t *pamh, const char *user, pld_ctx **pctx)
+{
+       pld_ctx *ctx = NULL;
+       int rc;
+
+       if (pam_get_data(pamh, PLD_CTX, (CONST_ARG void **)&ctx) == PAM_SUCCESS) {
+               if (ctx->user && strcmp(ctx->user, user)) {
+                       pam_clr_ctx(ctx);
+               }
+               rc = PAM_SUCCESS;
+       }
+       if (!ctx) {
+               ctx = calloc(1, sizeof(*ctx));
+               if (!ctx)
+                       return PAM_BUF_ERR;
+               rc = pam_set_data(pamh, PLD_CTX, ctx, pam_del_ctx);
+               if (rc != PAM_SUCCESS)
+                       pam_del_ctx(pamh, ctx, 0);
+       }
+       if (rc == PAM_SUCCESS)
+               *pctx = ctx;
+       return rc;
+}
+
+static int pam_get_authtok(
+       pam_handle_t *pamh, int flags, char *prompt1, char *prompt2, char **pwd)
+{
+       int rc;
+       char *p;
+       struct pam_message msg[1], *pmsg[1];
+       struct pam_response *resp;
+       struct pam_conv *conv;
+
+       *pwd = NULL;
+
+       rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &conv);
+       if (rc == PAM_SUCCESS) {
+               pmsg[0] = &msg[0];
+               msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
+               msg[0].msg = prompt1;
+               resp = NULL;
+               rc = conv->conv (1,
+                        (CONST_ARG struct pam_message **) pmsg,
+                        &resp, conv->appdata_ptr);
+       } else {
+               return rc;
+       }
+
+       if (resp != NULL) {
+               if ((flags & PAM_DISALLOW_NULL_AUTHTOK) && resp[0].resp == NULL)
+               {
+                       free (resp);
+                       return PAM_AUTH_ERR;
+               }
+
+               p = resp[0].resp;
+               resp[0].resp = NULL;
+               free (resp);
+       } else {
+               return PAM_CONV_ERR;
+       }
+
+       if (prompt2) {
+               msg[0].msg = prompt2;
+               resp = NULL;
+               rc = conv->conv (1,
+                        (CONST_ARG struct pam_message **) pmsg,
+                        &resp, conv->appdata_ptr);
+               if (resp && resp[0].resp && !strcmp(resp[0].resp, p))
+                       rc = PAM_SUCCESS;
+               else
+                       rc = PAM_AUTHTOK_RECOVERY_ERR;
+               if (resp) {
+                       if (resp[0].resp) {
+                               (void) memset(resp[0].resp, 0, strlen(resp[0].resp));
+                               free(resp[0].resp);
+                       }
+                       free(resp);
+               }
+       }
+
+       if (rc == PAM_SUCCESS)
+               *pwd = p;
+       else if (p) {
+               memset(p, 0, strlen(p));
+               free(p);
+       }
+
+       return rc;
+}
+
+static enum nss_status pam_read_authc(
+       TFILE *fp,pld_ctx *ctx,int *errnop)
+{
+       char *buffer = ctx->buf, *user;
+       size_t buflen = sizeof(ctx->buf);
+       size_t bufptr = 0;
+       int32_t tmpint32;
+
+       READ_STRING_BUF(fp,user);
+       READ_STRING_BUF(fp,ctx->dn);
+       READ_INT32(fp,ctx->authok);
+       READ_INT32(fp,ctx->authz);
+       READ_STRING_BUF(fp,ctx->authzmsg);
+       ctx->authok = nslcd2pam_rc(ctx->authok);
+       ctx->authz = nslcd2pam_rc(ctx->authz);
+       return NSS_STATUS_SUCCESS;
+}
+
+static enum nss_status pam_do_authc(
+       pld_ctx *ctx, const char *user, const char *svc,const char *pwd,int *errnop)
+{
+       NSS_BYGEN(NSLCD_ACTION_PAM_AUTHC,
+               WRITE_STRING(fp,user);
+               WRITE_STRING(fp,"" /* DN */);
+               WRITE_STRING(fp,svc);
+               WRITE_STRING(fp,pwd),
+               pam_read_authc(fp,ctx,errnop));
+}
+
+#define        USE_FIRST       1
+#define        TRY_FIRST       2
+#define        USE_TOKEN       4
+
+int pam_sm_authenticate(
+       pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+       int err, rc;
+       const char *username, *svc;
+       char *p = NULL;
+       int first_pass = 0, ignore_flags = 0;
+       int i;
+       pld_ctx *ctx;
+
+       for (i = 0; i < argc; i++) {
+               if (!strcmp (argv[i], "use_first_pass"))
+                       first_pass |= USE_FIRST;
+               else if (!strcmp (argv[i], "try_first_pass"))
+                       first_pass |= TRY_FIRST;
+               else if (!strcmp (argv[i], "ignore_unknown_user"))
+                       ignore_flags |= IGNORE_UNKNOWN;
+               else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
+                       ignore_flags |= IGNORE_UNAVAIL;
+               else if (!strcmp (argv[i], "no_warn"))
+                       ;
+               else if (!strcmp (argv[i], "debug"))
+                       ;
+               else
+                       syslog (LOG_ERR, "illegal option %s", argv[i]);
+       }
+
+       rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_get_ctx(pamh, username, &ctx);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_get_item (pamh, PAM_SERVICE, (CONST_ARG void **) &svc);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       for (i=0;i<2;i++) {
+               if (!first_pass) {
+                       rc = pam_get_authtok(pamh, flags, i ? "LDAP Password: " :
+                               "Password: ", NULL, &p);
+                       i = 2;
+                       if (rc == PAM_SUCCESS) {
+                               pam_set_item(pamh, PAM_AUTHTOK, p);
+                               memset(p, 0, strlen(p));
+                               free(p);
+                       } else {
+                               break;
+                       }
+               }
+               rc = pam_get_item (pamh, PAM_AUTHTOK, (CONST_ARG void **) &p);
+               if (rc == PAM_SUCCESS) {
+                       rc = pam_do_authc(ctx, username, svc, p, &err);
+                       NSS2PAM_RC(rc, ignore_flags, ctx->authok);
+               }
+               if (rc == PAM_SUCCESS || (first_pass & USE_FIRST)) {
+                       break;
+               }
+               first_pass = 0;
+       }
+
+       if (rc == PAM_SUCCESS) {
+               ctx->user = strdup(username);
+               if (ctx->authz == PAM_NEW_AUTHTOK_REQD)
+                       ctx->oldpw = strdup(p);
+       }
+
+       return rc;
+}
+
+int pam_sm_setcred(
+       pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+       return PAM_SUCCESS;
+}
+
+static int
+pam_warn(
+       struct pam_conv *aconv, const char *message, int style, int no_warn)
+{
+  struct pam_message msg, *pmsg;
+  struct pam_response *resp;
+
+  if (no_warn)
+    return PAM_SUCCESS;
+
+  pmsg = &msg;
+
+  msg.msg_style = style;
+  msg.msg = (char *) message;
+  resp = NULL;
+
+  return aconv->conv (1,
+                     (CONST_ARG struct pam_message **) &pmsg,
+                     &resp, aconv->appdata_ptr);
+}
+
+static enum nss_status pam_read_authz(
+       TFILE *fp,pld_ctx *ctx,int *errnop)
+{
+       char *buffer = ctx->buf;
+       size_t buflen = sizeof(ctx->buf);
+       size_t bufptr = 0;
+       int32_t tmpint32;
+
+       READ_STRING_BUF(fp,ctx->tmpluser);
+       READ_STRING_BUF(fp,ctx->dn);
+       READ_INT32(fp,ctx->authz);
+       READ_STRING_BUF(fp,ctx->authzmsg);
+       ctx->authz = nslcd2pam_rc(ctx->authz);
+       return NSS_STATUS_SUCCESS;
+}
+
+static enum nss_status pam_do_authz(
+       pld_ctx *ctx, const char *svc, const char *ruser, const char *rhost,
+       const char *tty, int *errnop)
+{
+       NSS_BYGEN(NSLCD_ACTION_PAM_AUTHZ,
+               WRITE_STRING(fp,ctx->user);
+               WRITE_STRING(fp,ctx->dn);
+               WRITE_STRING(fp,svc);
+               WRITE_STRING(fp,ruser);
+               WRITE_STRING(fp,rhost);
+               WRITE_STRING(fp,tty),
+               pam_read_authz(fp,ctx,errnop));
+}
+
+int pam_sm_acct_mgmt(
+       pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+       int rc, err;
+       const char *username, *svc, *ruser, *rhost, *tty;
+       int no_warn = 0, ignore_flags = 0;
+       int i;
+       struct pam_conv *appconv;
+       pld_ctx *ctx = NULL, ctx2;
+
+       for (i = 0; i < argc; i++)
+       {
+               if (!strcmp (argv[i], "use_first_pass"))
+                       ;
+               else if (!strcmp (argv[i], "try_first_pass"))
+                       ;
+               else if (!strcmp (argv[i], "no_warn"))
+                       no_warn = 1;
+               else if (!strcmp (argv[i], "ignore_unknown_user"))
+                       ignore_flags |= IGNORE_UNKNOWN;
+               else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
+                       ignore_flags |= IGNORE_UNAVAIL;
+               else if (!strcmp (argv[i], "debug"))
+                       ;
+               else
+                       syslog (LOG_ERR, "illegal option %s", argv[i]);
+       }
+
+       if (flags & PAM_SILENT)
+               no_warn = 1;
+
+       rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &appconv);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       if (username == NULL)
+               return PAM_USER_UNKNOWN;
+
+       rc = pam_get_ctx(pamh, username, &ctx);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_get_item (pamh, PAM_SERVICE, (CONST_ARG void **) &svc);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_get_item (pamh, PAM_RUSER, (CONST_ARG void **) &ruser);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_get_item (pamh, PAM_RHOST, (CONST_ARG void **) &rhost);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_get_item (pamh, PAM_TTY, (CONST_ARG void **) &tty);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       ctx2.dn = ctx->dn;
+       ctx2.user = ctx->user;
+       rc = pam_do_authz(&ctx2, svc, ruser, rhost, tty, &err);
+       NSS2PAM_RC(rc, ignore_flags, PAM_SUCCESS);
+       if (rc != PAM_SUCCESS) {
+               if (rc != PAM_IGNORE)
+                       pam_warn(appconv, "LDAP authorization failed", PAM_ERROR_MSG, no_warn);
+       } else {
+               if (ctx2.authzmsg && ctx2.authzmsg[0])
+                       pam_warn(appconv, ctx2.authzmsg, PAM_TEXT_INFO, no_warn);
+               if (ctx2.authz == PAM_SUCCESS) {
+                       rc = ctx->authz;
+                       if (ctx->authzmsg && ctx->authzmsg[0])
+                               pam_warn(appconv, ctx->authzmsg, PAM_TEXT_INFO, no_warn);
+               }
+       }
+       if ( rc == PAM_SUCCESS && ctx->tmpluser && ctx->tmpluser[0] ) {
+               rc = pam_set_item(pamh, PAM_USER, ctx->tmpluser);
+       }
+       return rc;
+}
+
+static enum nss_status pam_read_sess(
+       TFILE *fp,pld_ctx *ctx,int *errnop)
+{
+       int tmpint32;
+       READ_INT32(fp,ctx->sessid);
+       return NSS_STATUS_SUCCESS;
+}
+
+static enum nss_status pam_do_sess(
+       pam_handle_t *pamh,pld_ctx *ctx,int action,int *errnop)
+{
+       const char *svc = NULL, *tty = NULL, *rhost = NULL, *ruser = NULL;
+       
+       pam_get_item (pamh, PAM_SERVICE, (CONST_ARG void **) &svc);
+       pam_get_item (pamh, PAM_TTY, (CONST_ARG void **) &tty);
+       pam_get_item (pamh, PAM_RHOST, (CONST_ARG void **) &rhost);
+       pam_get_item (pamh, PAM_RUSER, (CONST_ARG void **) &ruser);
+
+       {
+       NSS_BYGEN(action,
+               WRITE_STRING(fp,ctx->user);
+               WRITE_STRING(fp,ctx->dn);
+               WRITE_STRING(fp,svc);
+               WRITE_STRING(fp,tty);
+               WRITE_STRING(fp,rhost);
+               WRITE_STRING(fp,ruser);
+               WRITE_INT32(fp,ctx->sessid),
+               pam_read_sess(fp,ctx,errnop));
+       }
+}
+
+static int pam_sm_session(
+       pam_handle_t *pamh, int flags, int argc, const char **argv,
+       int action, int *no_warn)
+{
+       int rc, err;
+       const char *username;
+       int ignore_flags = 0;
+       int i, success = PAM_SUCCESS;
+       pld_ctx *ctx = NULL;
+
+       for (i = 0; i < argc; i++)
+       {
+               if (!strcmp (argv[i], "use_first_pass"))
+                       ;
+               else if (!strcmp (argv[i], "try_first_pass"))
+                       ;
+               else if (!strcmp (argv[i], "no_warn"))
+                       *no_warn = 1;
+               else if (!strcmp (argv[i], "ignore_unknown_user"))
+                       ignore_flags |= IGNORE_UNKNOWN;
+               else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
+                       ignore_flags |= IGNORE_UNAVAIL;
+               else if (!strcmp (argv[i], "debug"))
+                       ;
+               else
+                       syslog (LOG_ERR, "illegal option %s", argv[i]);
+       }
+
+       if (flags & PAM_SILENT)
+               *no_warn = 1;
+
+       rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       if (username == NULL)
+               return PAM_USER_UNKNOWN;
+
+       rc = pam_get_ctx(pamh, username, &ctx);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_do_sess(pamh, ctx, action, &err);
+       NSS2PAM_RC(rc, ignore_flags, PAM_SUCCESS);
+       return rc;
+}
+
+int pam_sm_open_session(
+       pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+       int rc, no_warn = 0;
+       struct pam_conv *appconv;
+
+       rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &appconv);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_sm_session(pamh,flags,argc,argv,NSLCD_ACTION_PAM_SESS_O,&no_warn);
+       if (rc != PAM_SUCCESS && rc != PAM_IGNORE)
+               pam_warn(appconv, "LDAP open_session failed", PAM_ERROR_MSG, no_warn);
+       return rc;
+}
+
+int pam_sm_close_session(
+       pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+       int rc, no_warn = 0;;
+       struct pam_conv *appconv;
+
+       rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &appconv);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_sm_session(pamh,flags,argc,argv,NSLCD_ACTION_PAM_SESS_C,&no_warn);
+       if (rc != PAM_SUCCESS && rc != PAM_IGNORE)
+               pam_warn(appconv, "LDAP close_session failed", PAM_ERROR_MSG, no_warn);
+       return rc;
+}
+
+static enum nss_status pam_read_pwmod(
+       TFILE *fp,pld_ctx *ctx,int *errnop)
+{
+       char *buffer = ctx->buf, *user;
+       size_t buflen = sizeof(ctx->buf);
+       size_t bufptr = 0;
+       int32_t tmpint32;
+
+       READ_STRING_BUF(fp,user);
+       READ_STRING_BUF(fp,ctx->dn);
+       READ_INT32(fp,ctx->authz);
+       READ_STRING_BUF(fp,ctx->authzmsg);
+       ctx->authz = nslcd2pam_rc(ctx->authz);
+       return NSS_STATUS_SUCCESS;
+}
+
+static enum nss_status pam_do_pwmod(
+       pld_ctx *ctx, const char *user, const char *svc,
+       const char *oldpw, const char *newpw, int *errnop)
+{
+       NSS_BYGEN(NSLCD_ACTION_PAM_PWMOD,
+               WRITE_STRING(fp,user);
+               WRITE_STRING(fp,ctx->dn);
+               WRITE_STRING(fp,svc);
+               WRITE_STRING(fp,oldpw);
+               WRITE_STRING(fp,newpw),
+               pam_read_pwmod(fp,ctx,errnop));
+}
+
+int pam_sm_chauthtok(
+       pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+       int rc, err;
+       const char *username, *p = NULL, *q = NULL, *svc;
+       int first_pass = 0, no_warn = 0, ignore_flags = 0;
+       int i, success = PAM_SUCCESS;
+       struct pam_conv *appconv;
+       pld_ctx *ctx = NULL;
+
+       for (i = 0; i < argc; i++)
+       {
+               if (!strcmp (argv[i], "use_first_pass"))
+                       first_pass |= USE_FIRST;
+               else if (!strcmp (argv[i], "try_first_pass"))
+                       first_pass |= TRY_FIRST;
+               else if (!strcmp (argv[i], "use_authtok"))
+                       first_pass |= USE_TOKEN;
+               else if (!strcmp (argv[i], "no_warn"))
+                       no_warn = 1;
+               else if (!strcmp (argv[i], "ignore_unknown_user"))
+                       ignore_flags |= IGNORE_UNKNOWN;
+               else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
+                       ignore_flags |= IGNORE_UNAVAIL;
+               else if (!strcmp (argv[i], "debug"))
+                       ;
+               else
+                       syslog (LOG_ERR, "illegal option %s", argv[i]);
+       }
+
+       if (flags & PAM_SILENT)
+               no_warn = 1;
+
+       rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &appconv);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       if (username == NULL)
+               return PAM_USER_UNKNOWN;
+
+       rc = pam_get_ctx(pamh, username, &ctx);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       rc = pam_get_item (pamh, PAM_SERVICE, (CONST_ARG void **) &svc);
+       if (rc != PAM_SUCCESS)
+               return rc;
+
+       if (flags & PAM_PRELIM_CHECK) {
+               if (getuid()) {
+                       if (!first_pass) {
+                               rc = pam_get_authtok(pamh, flags, "(current) LDAP Password: ",
+                                       NULL, &p);
+                               if (rc == PAM_SUCCESS) {
+                                       pam_set_item(pamh, PAM_OLDAUTHTOK, p);
+                                       memset(p, 0, strlen(p));
+                                       free(p);
+                               }
+                       }
+                       rc = pam_get_item(pamh, PAM_OLDAUTHTOK, &p);
+                       if (rc) return rc;
+               } else {
+                       rc = PAM_SUCCESS;
+               }
+               if (!ctx->dn) {
+                       rc = pam_do_pwmod(ctx, username, svc, p, NULL, &err);
+                       NSS2PAM_RC(rc, ignore_flags, PAM_SUCCESS);
+               }
+               return rc;
+       }
+
+       rc = pam_get_item(pamh, PAM_OLDAUTHTOK, &p);
+       if (rc) return rc;
+
+       if (!p)
+               p = ctx->oldpw;
+
+       if (first_pass) {
+               rc = pam_get_item(pamh, PAM_AUTHTOK, &q);
+               if ((rc != PAM_SUCCESS || !q) && (first_pass & (USE_FIRST|USE_TOKEN))) {
+                       if (rc == PAM_SUCCESS)
+                               rc = PAM_AUTHTOK_RECOVERY_ERR;
+                       return rc;
+               }
+       }
+       if (!q) {
+               rc = pam_get_authtok(pamh, flags, "Enter new LDAP Password: ",
+                       "Retype new LDAP Password: ", &q);
+               if (rc == PAM_SUCCESS) {
+                       pam_set_item(pamh, PAM_AUTHTOK, q);
+                       memset(q, 0, strlen(q));
+                       free(q);
+                       rc = pam_get_item(pamh, PAM_AUTHTOK, &q);
+               }
+               if (rc != PAM_SUCCESS)
+                       return rc;
+       }
+       rc = pam_do_pwmod(ctx, username, svc, p, q, &err);
+       p = NULL; q = NULL;
+       NSS2PAM_RC(rc, ignore_flags, PAM_SUCCESS);
+       if (rc == PAM_SUCCESS) {
+               rc = ctx->authz;
+               if (rc != PAM_SUCCESS)
+                       pam_warn(appconv, ctx->authzmsg, PAM_ERROR_MSG, no_warn);
+       } else if (rc != PAM_IGNORE)
+               pam_warn(appconv, "LDAP pwmod failed", PAM_ERROR_MSG, no_warn);
+       return rc;
+}
+
+#ifdef PAM_STATIC
+struct pam_module _modstruct = {
+       "pam_ldapd",
+       pam_sm_authenticate,
+       pam_sm_setcred,
+       pam_sm_acct_mgmt,
+       pam_sm_open_session,
+       pam_sm_close_session,
+       pam_sm_chauthtok
+};
+#endif /* PAM_STATIC */
index 9e4d6fead3e5407810b5e1ccc7290d3d4a366a17..08f318d65e578eef354af8c1e616c15d27284fbd 100644 (file)
@@ -1,7 +1,7 @@
 /* nssov.c - nss-ldap overlay for slapd */
 /* $OpenLDAP$ */
 /*
- * Copyright 2008 by Howard Chu, Symas Corp.
+ * Copyright 2008-2009 by Howard Chu, Symas Corp.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,9 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 
+AttributeDescription *nssov_pam_host_ad;
+AttributeDescription *nssov_pam_svc_ad;
+
 /* buffer sizes for I/O */
 #define READBUFFER_MINSIZE 32
 #define READBUFFER_MAXSIZE 64
@@ -152,7 +155,7 @@ int write_address(TFILE *fp,struct berval *addr)
                /* failure, log but write simple invalid address
                         (otherwise the address list is messed up) */
                /* TODO: have error message in correct format */
-               Debug(LDAP_DEBUG_ANY,"nssov: unparseable address: %s",addr->bv_val,0,0);
+               Debug(LDAP_DEBUG_ANY,"nssov: unparseable address: %s\n",addr->bv_val,0,0);
                /* write an illegal address type */
                WRITE_INT32(fp,-1);
                /* write an empty address */
@@ -170,14 +173,14 @@ int read_address(TFILE *fp,char *addr,int *addrlen,int *af)
        READ_INT32(fp,*af);
        if ((*af!=AF_INET)&&(*af!=AF_INET6))
        {
-               Debug(LDAP_DEBUG_ANY,"nssov: incorrect address family specified: %d",*af,0,0);
+               Debug(LDAP_DEBUG_ANY,"nssov: incorrect address family specified: %d\n",*af,0,0);
                return -1;
        }
        /* read address length */
        READ_INT32(fp,len);
        if ((len>*addrlen)||(len<=0))
        {
-               Debug(LDAP_DEBUG_ANY,"nssov: address length incorrect: %d",len,0,0);
+               Debug(LDAP_DEBUG_ANY,"nssov: address length incorrect: %d\n",len,0,0);
                return -1;
        }
        *addrlen=len;
@@ -237,7 +240,7 @@ static int read_header(TFILE *fp,int32_t *action)
   READ_TYPE(fp,tmpint32,int32_t);
   if (tmpint32 != (int32_t)NSLCD_VERSION)
   {
-    Debug( LDAP_DEBUG_TRACE,"nssov: wrong nslcd version id (%d)",(int)tmpint32,0,0);
+    Debug( LDAP_DEBUG_TRACE,"nssov: wrong nslcd version id (%d)\n",(int)tmpint32,0,0);
     return -1;
   }
   /* read the request type */
@@ -258,9 +261,9 @@ static void handleconnection(nssov_info *ni,int sock,Operation *op)
 
   /* log connection */
   if (lutil_getpeereid(sock,&uid,&gid))
-    Debug( LDAP_DEBUG_TRACE,"nssov: connection from unknown client: %s",strerror(errno),0,0);
+    Debug( LDAP_DEBUG_TRACE,"nssov: connection from unknown client: %s\n",strerror(errno),0,0);
   else
-    Debug( LDAP_DEBUG_TRACE,"nssov: connection from uid=%d gid=%d",
+    Debug( LDAP_DEBUG_TRACE,"nssov: connection from uid=%d gid=%d\n",
                       (int)uid,(int)gid,0);
 
   /* Should do authid mapping too */
@@ -322,6 +325,11 @@ static void handleconnection(nssov_info *ni,int sock,Operation *op)
     case NSLCD_ACTION_SERVICE_ALL:      (void)nssov_service_all(ni,fp,op); break;
     case NSLCD_ACTION_SHADOW_BYNAME:    if (uid==0) (void)nssov_shadow_byname(ni,fp,op); break;
     case NSLCD_ACTION_SHADOW_ALL:       if (uid==0) (void)nssov_shadow_all(ni,fp,op); break;
+       case NSLCD_ACTION_PAM_AUTHC:            (void)pam_authc(ni,fp,op); break;
+       case NSLCD_ACTION_PAM_AUTHZ:            (void)pam_authz(ni,fp,op); break;
+       case NSLCD_ACTION_PAM_SESS_O:           if (uid==0) (void)pam_sess_o(ni,fp,op); break;
+       case NSLCD_ACTION_PAM_SESS_C:           if (uid==0) (void)pam_sess_c(ni,fp,op); break;
+       case NSLCD_ACTION_PAM_PWMOD:            (void)pam_pwmod(ni,fp,op); break;
     default:
       Debug( LDAP_DEBUG_ANY,"nssov: invalid request id: %d",(int)action,0,0);
       break;
@@ -380,6 +388,7 @@ static void *acceptconn(void *ctx, void *arg)
        }
        connection_fake_init( &conn, &opbuf, ctx );
        op=&opbuf.ob_op;
+       conn.c_ssf = conn.c_transport_ssf = local_ssf;
        op->o_bd = ni->ni_db;
        op->o_tag = LDAP_REQ_SEARCH;
 
@@ -402,9 +411,22 @@ static slap_verbmasks nss_svcs[] = {
        { BER_BVNULL, 0 }
 };
 
+static slap_verbmasks pam_opts[] = {
+       { BER_BVC("userhost"), NI_PAM_USERHOST },
+       { BER_BVC("userservice"), NI_PAM_USERSVC },
+       { BER_BVC("usergroup"), NI_PAM_USERGRP },
+       { BER_BVC("hostservice"), NI_PAM_HOSTSVC },
+       { BER_BVC("authz2dn"), NI_PAM_SASL2DN },
+       { BER_BVC("uid2dn"), NI_PAM_UID2DN },
+       { BER_BVNULL, 0 }
+};
+
 enum {
        NSS_SSD=1,
-       NSS_MAP
+       NSS_MAP,
+       NSS_PAM,
+       NSS_PAMGROUP,
+       NSS_PAMSESS
 };
 
 static ConfigDriver nss_cf_gen;
@@ -420,6 +442,57 @@ static ConfigTable nsscfg[] = {
                        "DESC 'Map <service> lookups of <orig> attr to <new> attr' "
                        "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString )", NULL, NULL },
+       { "nssov-pam", "options", 2, 0, 0, ARG_MAGIC|NSS_PAM,
+               nss_cf_gen, "(OLcfgCtAt:3.3 NAME 'olcNssPam' "
+                       "DESC 'PAM authentication and authorization options' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString )", NULL, NULL },
+       { "nssov-pam-defhost", "hostname", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
+               (void *)offsetof(struct nssov_info, ni_pam_defhost),
+               "(OLcfgCtAt:3.4 NAME 'olcNssPamDefHost' "
+                       "DESC 'Default hostname for service checks' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+       { "nssov-pam-group-dn", "DN", 2, 2, 0, ARG_MAGIC|ARG_DN|NSS_PAMGROUP,
+               nss_cf_gen, "(OLcfgCtAt:3.5 NAME 'olcNssPamGroupDN' "
+                       "DESC 'DN of group in which membership is required' "
+                       "EQUALITY distinguishedNameMatch "
+                       "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+       { "nssov-pam-group-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
+               (void *)offsetof(struct nssov_info, ni_pam_group_ad),
+               "(OLcfgCtAt:3.6 NAME 'olcNssPamGroupAD' "
+                       "DESC 'Member attribute to use for group check' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+       { "nssov-pam-min-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
+               (void *)offsetof(struct nssov_info, ni_pam_min_uid),
+               "(OLcfgCtAt:3.7 NAME 'olcNssPamMinUid' "
+                       "DESC 'Minimum UID allowed to login' "
+                       "EQUALITY integerMatch "
+                       "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+       { "nssov-pam-max-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
+               (void *)offsetof(struct nssov_info, ni_pam_max_uid),
+               "(OLcfgCtAt:3.8 NAME 'olcNssPamMaxUid' "
+                       "DESC 'Maximum UID allowed to login' "
+                       "EQUALITY integerMatch "
+                       "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+       { "nssov-pam-template-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
+               (void *)offsetof(struct nssov_info, ni_pam_template_ad),
+               "(OLcfgCtAt:3.9 NAME 'olcNssPamTemplateAD' "
+                       "DESC 'Attribute to use for template login name' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+       { "nssov-pam-template", "name", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
+               (void *)offsetof(struct nssov_info, ni_pam_template),
+               "(OLcfgCtAt:3.10 NAME 'olcNssPamTemplate' "
+                       "DESC 'Default template login name' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+       { "nssov-pam-session", "service", 2, 2, 0, ARG_MAGIC|ARG_BERVAL|NSS_PAMSESS,
+               nss_cf_gen, "(OLcfgCtAt:3.11 NAME 'olcNssPamSession' "
+                       "DESC 'Services for which sessions will be recorded' "
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString )", NULL, NULL },
        { NULL, NULL, 0,0,0, ARG_IGNORED }
 };
 
@@ -428,7 +501,10 @@ static ConfigOCs nssocs[] = {
                "NAME 'olcNssOvConfig' "
                "DESC 'NSS lookup configuration' "
                "SUP olcOverlayConfig "
-               "MAY ( olcNssSsd $ olcNssMap ) )",
+               "MAY ( olcNssSsd $ olcNssMap $ olcNssPam $ olcNssPamDefHost $ "
+                       "olcNssPamGroupDN $ olcNssPamGroupAD $ "
+                       "olcNssPamMinUid $ olcNssPamMaxUid $ olcNssPamSession $ "
+                       "olcNssPamTemplateAD $ olcNssPamTemplate ) )",
                Cft_Overlay, nsscfg },
        { NULL, 0, NULL }
 };
@@ -440,6 +516,7 @@ nss_cf_gen(ConfigArgs *c)
        nssov_info *ni = on->on_bi.bi_private;
        nssov_mapinfo *mi;
        int i, j, rc = 0;
+       slap_mask_t m;
 
        if ( c->op == SLAP_CONFIG_EMIT ) {
                switch(c->type) {
@@ -495,9 +572,28 @@ nss_cf_gen(ConfigArgs *c)
                                }
                        }
                        break;
+               case NSS_PAM:
+                       rc = mask_to_verbs( pam_opts, ni->ni_pam_opts, &c->rvalue_vals );
+                       break;
+               case NSS_PAMGROUP:
+                       if (!BER_BVISEMPTY( &ni->ni_pam_group_dn )) {
+                               value_add_one( &c->rvalue_vals, &ni->ni_pam_group_dn );
+                               value_add_one( &c->rvalue_nvals, &ni->ni_pam_group_dn );
+                       } else {
+                               rc = 1;
+                       }
+                       break;
+               case NSS_PAMSESS:
+                       if (ni->ni_pam_sessions) {
+                               ber_bvarray_dup_x( &c->rvalue_vals, ni->ni_pam_sessions, NULL );
+                       } else {
+                               rc = 1;
+                       }
+                       break;
                }
                return rc;
        } else if ( c->op == LDAP_MOD_DELETE ) {
+               /* FIXME */
                return 1;
        }
        switch( c->type ) {
@@ -558,6 +654,44 @@ nss_cf_gen(ConfigArgs *c)
                        }
                }
                break;
+       case NSS_PAM:
+               m = ni->ni_pam_opts;
+               i = verbs_to_mask(c->argc, c->argv, pam_opts, &m);
+               if (i == 0) {
+                       ni->ni_pam_opts = m;
+                       if ((m & NI_PAM_USERHOST) && !nssov_pam_host_ad) {
+                               const char *text;
+                               i = slap_str2ad("host", &nssov_pam_host_ad, &text);
+                               if (i != LDAP_SUCCESS) {
+                                       snprintf(c->cr_msg, sizeof(c->cr_msg),
+                                               "nssov: host attr unknown: %s", text);
+                                       Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg,0,0);
+                                       rc = 1;
+                                       break;
+                               }
+                       }
+                       if ((m & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) && !nssov_pam_svc_ad) {
+                               const char *text;
+                               i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
+                               if (i != LDAP_SUCCESS) {
+                                       snprintf(c->cr_msg, sizeof(c->cr_msg),
+                                               "nssov: authorizedService attr unknown: %s", text);
+                                       Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg,0,0);
+                                       rc = 1;
+                                       break;
+                               }
+                       }
+               } else {
+                       rc = 1;
+               }
+               break;
+       case NSS_PAMGROUP:
+               ni->ni_pam_group_dn = c->value_ndn;
+               ch_free( c->value_dn.bv_val );
+               break;
+       case NSS_PAMSESS:
+               ber_bvarray_add( &ni->ni_pam_sessions, &c->value_bv );
+               break;
        }
        return rc;
 }
@@ -570,9 +704,12 @@ nssov_db_init(
        slap_overinst *on = (slap_overinst *)be->bd_info;
        nssov_info *ni;
        nssov_mapinfo *mi;
-       int i, j;
+       int rc;
 
-       ni = ch_malloc( sizeof(nssov_info) );
+       rc = nssov_pam_init();
+       if (rc) return rc;
+
+       ni = ch_calloc( 1, sizeof(nssov_info) );
        on->on_bi.bi_private = ni;
 
        /* set up map keys */
@@ -589,6 +726,7 @@ nssov_db_init(
        nssov_shadow_init(ni);
 
        ni->ni_db = be->bd_self;
+       ni->ni_pam_opts = NI_PAM_UID2DN;
 
        return 0;
 }
@@ -639,17 +777,47 @@ nssov_db_open(
                mi->mi_attrs[j].an_desc = NULL;
        }
 
+       /* Find host and authorizedService definitions */
+       if ((ni->ni_pam_opts & NI_PAM_USERHOST) && !nssov_pam_host_ad)
+       {
+               const char *text;
+               i = slap_str2ad("host", &nssov_pam_host_ad, &text);
+               if (i != LDAP_SUCCESS) {
+                       Debug(LDAP_DEBUG_ANY,"nssov: host attr unknown: %s\n",
+                               text, 0, 0 );
+                       return -1;
+               }
+       }
+       if ((ni->ni_pam_opts & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) &&
+               !nssov_pam_svc_ad)
+       {
+               const char *text;
+               i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
+               if (i != LDAP_SUCCESS) {
+                       Debug(LDAP_DEBUG_ANY,"nssov: authorizedService attr unknown: %s\n",
+                               text, 0, 0 );
+                       return -1;
+               }
+       }
        if ( slapMode & SLAP_SERVER_MODE ) {
+               /* make sure /var/run/nslcd exists */
+               if (mkdir(NSLCD_PATH, (mode_t) 0555)) {
+                       Debug(LDAP_DEBUG_TRACE,"nssov: mkdir(%s) failed (ignored): %s\n",
+                                       NSLCD_PATH,strerror(errno),0);
+               } else {
+                       Debug(LDAP_DEBUG_TRACE,"nssov: created %s\n",NSLCD_PATH,0,0);
+               }
+
                /* create a socket */
                if ( (sock=socket(PF_UNIX,SOCK_STREAM,0))<0 )
                {
-                       Debug(LDAP_DEBUG_ANY,"nssov: cannot create socket: %s",strerror(errno),0,0);
+                       Debug(LDAP_DEBUG_ANY,"nssov: cannot create socket: %s\n",strerror(errno),0,0);
                        return -1;
                }
                /* remove existing named socket */
                if (unlink(NSLCD_SOCKET)<0)
                {
-                       Debug( LDAP_DEBUG_TRACE,"nssov: unlink() of "NSLCD_SOCKET" failed (ignored): %s",
+                       Debug( LDAP_DEBUG_TRACE,"nssov: unlink() of "NSLCD_SOCKET" failed (ignored): %s\n",
                                                        strerror(errno),0,0);
                }
                /* create socket address structure */
index 01303ac57dd8fac3ddfaad9fd6f879435e297a20..6e65ce074559b0be433d2b91536eacf9de716179 100644 (file)
@@ -9,8 +9,12 @@
 #ifndef NSSOV_H
 #define NSSOV_H
 
+#ifndef NSLCD_PATH
+#define        NSLCD_PATH      "/var/run/nslcd"
+#endif
+
 #ifndef NSLCD_SOCKET
-#define NSLCD_SOCKET   "/var/run/nslcd/socket"
+#define NSLCD_SOCKET   NSLCD_PATH "/socket"
 #endif
 
 #include <stdio.h>
@@ -64,8 +68,32 @@ typedef struct nssov_info
        int ni_socket;
        Connection *ni_conn;
        BackendDB *ni_db;
+
+       /* PAM authz support... */
+       slap_mask_t ni_pam_opts;
+       struct berval ni_pam_group_dn;
+       AttributeDescription *ni_pam_group_ad;
+       int ni_pam_min_uid;
+       int ni_pam_max_uid;
+       AttributeDescription *ni_pam_template_ad;
+       struct berval ni_pam_template;
+       struct berval ni_pam_defhost;
+       struct berval *ni_pam_sessions;
 } nssov_info;
 
+#define NI_PAM_USERHOST                1       /* old style host checking */
+#define NI_PAM_USERSVC         2       /* old style service checking */
+#define NI_PAM_USERGRP         4       /* old style group checking */
+#define NI_PAM_HOSTSVC         8       /* new style authz checking */
+#define NI_PAM_SASL2DN         0x10    /* use sasl2dn */
+#define NI_PAM_UID2DN          0x20    /* use uid2dn */
+
+#define        NI_PAM_OLD      (NI_PAM_USERHOST|NI_PAM_USERSVC|NI_PAM_USERGRP)
+#define        NI_PAM_NEW      NI_PAM_HOSTSVC
+
+extern AttributeDescription *nssov_pam_host_ad;
+extern AttributeDescription *nssov_pam_svc_ad;
+
 /* Read the default configuration file. */
 void nssov_cfg_init(nssov_info *ni,const char *fname);
 
@@ -139,11 +167,12 @@ int read_address(TFILE *fp,char *addr,int *addrlen,int *af);
 /* checks to see if the specified string is a valid username */
 int isvalidusername(struct berval *name);
 
-/* transforms the DN info a uid doing an LDAP lookup if needed */
+/* transforms the DN into a uid doing an LDAP lookup if needed */
 int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid);
 
 /* transforms the uid into a DN by doing an LDAP lookup */
 int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn);
+int nssov_name2dn_cb(Operation *op, SlapReply *rs);
 
 /* Escapes characters in a string for use in a search filter. */
 int nssov_escape(struct berval *src,struct berval *dst);
@@ -163,6 +192,8 @@ void nssov_rpc_init(nssov_info *ni);
 void nssov_service_init(nssov_info *ni);
 void nssov_shadow_init(nssov_info *ni);
 
+int nssov_pam_init(void);
+
 /* these are the different functions that handle the database
    specific actions, see nslcd.h for the action descriptions */
 int nssov_alias_byname(nssov_info *ni,TFILE *fp,Operation *op);
@@ -195,6 +226,11 @@ int nssov_service_bynumber(nssov_info *ni,TFILE *fp,Operation *op);
 int nssov_service_all(nssov_info *ni,TFILE *fp,Operation *op);
 int nssov_shadow_byname(nssov_info *ni,TFILE *fp,Operation *op);
 int nssov_shadow_all(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_authc(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_authz(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op);
 
 /* config initialization */
 #define NSSOV_INIT(db) \
diff --git a/contrib/slapd-modules/nssov/pam.c b/contrib/slapd-modules/nssov/pam.c
new file mode 100644 (file)
index 0000000..d424999
--- /dev/null
@@ -0,0 +1,668 @@
+/* pam.c - pam processing routines */
+/* $OpenLDAP$ */
+/*
+ * Copyright 2009 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "nssov.h"
+#include "lutil.h"
+
+static int ppolicy_cid;
+static AttributeDescription *ad_loginStatus;
+
+const char *at_loginStatus =
+       "( 1.3.6.1.4.1.4745.1.20.1 "
+       "NAME ( 'loginStatus' ) "
+       "DESC 'Currently logged in sessions for a user' "
+       "EQUALITY caseIgnoreMatch "
+       "SUBSTR caseIgnoreSubstringsMatch "
+       "ORDERING caseIgnoreOrderingMatch "
+       "SYNTAX OMsDirectoryString "
+       "USAGE directoryOperation )";
+
+struct paminfo {
+       struct berval uid;
+       struct berval dn;
+       struct berval svc;
+       struct berval pwd;
+       int authz;
+       struct berval msg;
+};
+
+static int pam_bindcb(
+       Operation *op, SlapReply *rs)
+{
+       struct paminfo *pi = op->o_callback->sc_private;
+       LDAPControl *ctrl = ldap_control_find(LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
+               rs->sr_ctrls, NULL);
+       if (ctrl) {
+               LDAP *ld;
+               ber_int_t expire, grace;
+               LDAPPasswordPolicyError error;
+
+               ldap_create(&ld);
+               if (ld) {
+                       int rc = ldap_parse_passwordpolicy_control(ld,ctrl,
+                               &expire,&grace,&error);
+                       if (rc == LDAP_SUCCESS) {
+                               if (expire >= 0) {
+                                       char *unit = "seconds";
+                                       if (expire > 60) {
+                                               expire /= 60;
+                                               unit = "minutes";
+                                       }
+                                       if (expire > 60) {
+                                               expire /= 60;
+                                               unit = "hours";
+                                       }
+                                       if (expire > 24) {
+                                               expire /= 24;
+                                               unit = "days";
+                                       }
+#if 0  /* Who warns about expiration so far in advance? */
+                                       if (expire > 7) {
+                                               expire /= 7;
+                                               unit = "weeks";
+                                       }
+                                       if (expire > 4) {
+                                               expire /= 4;
+                                               unit = "months";
+                                       }
+                                       if (expire > 12) {
+                                               expire /= 12;
+                                               unit = "years";
+                                       }
+#endif
+                                       pi->msg.bv_len = sprintf(pi->msg.bv_val,
+                                               "\nWARNING: Password expires in %d %s\n", expire, unit);
+                               } else if (grace > 0) {
+                                       pi->msg.bv_len = sprintf(pi->msg.bv_val,
+                                               "Password expired; %d grace logins remaining",
+                                               grace);
+                                       pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
+                               } else if (error != PP_noError) {
+                                       ber_str2bv(ldap_passwordpolicy_err2txt(error), 0, 0,
+                                               &pi->msg);
+                                       switch (error) {
+                                       case PP_passwordExpired:
+                                               /* report this during authz */
+                                               rs->sr_err = LDAP_SUCCESS;
+                                               /* fallthru */
+                                       case PP_changeAfterReset:
+                                               pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
+                                       }
+                               }
+                       }
+                       ldap_ld_free(ld,0,NULL,NULL);
+               }
+       }
+       return LDAP_SUCCESS;
+}
+
+int pam_do_bind(nssov_info *ni,TFILE *fp,Operation *op,
+       struct paminfo *pi)
+{
+       int rc;
+       slap_callback cb = {0};
+       SlapReply rs = {REP_RESULT};
+       struct berval sdn;
+
+       pi->msg.bv_val = pi->pwd.bv_val;
+       pi->msg.bv_len = 0;
+       pi->authz = NSLCD_PAM_SUCCESS;
+       BER_BVZERO(&pi->dn);
+
+       if (!isvalidusername(&pi->uid)) {
+               Debug(LDAP_DEBUG_ANY,"nssov_pam_do_bind(%s): invalid user name\n",
+                       pi->uid.bv_val,0,0);
+               rc = NSLCD_PAM_USER_UNKNOWN;
+               goto finish;
+       }
+
+       if (ni->ni_pam_opts & NI_PAM_SASL2DN) {
+               int hlen = global_host_bv.bv_len;
+
+               /* cn=<service>+uid=<user>,cn=<host>,cn=pam,cn=auth */
+               sdn.bv_len = pi->uid.bv_len + pi->svc.bv_len + hlen +
+                       STRLENOF( "cn=+uid=,cn=,cn=pam,cn=auth" );
+               sdn.bv_val = op->o_tmpalloc( sdn.bv_len + 1, op->o_tmpmemctx );
+               sprintf(sdn.bv_val, "cn=%s+uid=%s,cn=%s,cn=pam,cn=auth",
+                       pi->svc.bv_val, pi->uid.bv_val, global_host_bv.bv_val);
+               slap_sasl2dn(op, &sdn, &pi->dn, 0);
+               op->o_tmpfree( sdn.bv_val, op->o_tmpmemctx );
+       }
+
+       /* If no luck, do a basic uid search */
+       if (BER_BVISEMPTY(&pi->dn) && (ni->ni_pam_opts & NI_PAM_UID2DN)) {
+               nssov_uid2dn(op, ni, &pi->uid, &pi->dn);
+               if (!BER_BVISEMPTY(&pi->dn)) {
+                       sdn = pi->dn;
+                       dnNormalize( 0, NULL, NULL, &sdn, &pi->dn, op->o_tmpmemctx );
+               }
+       }
+       BER_BVZERO(&sdn);
+       if (BER_BVISEMPTY(&pi->dn)) {
+               rc = NSLCD_PAM_USER_UNKNOWN;
+               goto finish;
+       }
+
+       if (BER_BVISEMPTY(&pi->pwd)) {
+               rc = NSLCD_PAM_IGNORE;
+               goto finish;
+       }
+
+       /* Should only need to do this once at open time, but there's always
+        * the possibility that ppolicy will get loaded later.
+        */
+       if (!ppolicy_cid) {
+               rc = slap_find_control_id(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+                       &ppolicy_cid);
+       }
+       /* of course, 0 is a valid cid, but it won't be ppolicy... */
+       if (ppolicy_cid) {
+               op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
+       }
+       cb.sc_response = pam_bindcb;
+       cb.sc_private = pi;
+       op->o_callback = &cb;
+       op->o_dn.bv_val[0] = 0;
+       op->o_dn.bv_len = 0;
+       op->o_ndn.bv_val[0] = 0;
+       op->o_ndn.bv_len = 0;
+       op->o_tag = LDAP_REQ_BIND;
+       op->o_protocol = LDAP_VERSION3;
+       op->orb_method = LDAP_AUTH_SIMPLE;
+       op->orb_cred = pi->pwd;
+       op->o_req_dn = pi->dn;
+       op->o_req_ndn = pi->dn;
+       slap_op_time( &op->o_time, &op->o_tincr );
+       rc = op->o_bd->be_bind( op, &rs );
+       memset(pi->pwd.bv_val,0,pi->pwd.bv_len);
+       /* quirk: on successful bind, caller has to send result. we need
+        * to make sure callbacks run.
+        */
+       if (rc == LDAP_SUCCESS)
+               send_ldap_result(op, &rs);
+       switch(rs.sr_err) {
+       case LDAP_SUCCESS: rc = NSLCD_PAM_SUCCESS; break;
+       case LDAP_INVALID_CREDENTIALS: rc = NSLCD_PAM_AUTH_ERR; break;
+       default: rc = NSLCD_PAM_AUTH_ERR; break;
+       }
+finish:
+       return rc;
+}
+
+int pam_authc(nssov_info *ni,TFILE *fp,Operation *op)
+{
+       int32_t tmpint32;
+       int rc;
+       slap_callback cb = {0};
+       SlapReply rs = {REP_RESULT};
+       char dnc[1024];
+       char uidc[32];
+       char svcc[256];
+       char pwdc[256];
+       struct berval sdn, dn;
+       struct paminfo pi;
+
+
+       READ_STRING_BUF2(fp,uidc,sizeof(uidc));
+       pi.uid.bv_val = uidc;
+       pi.uid.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,dnc,sizeof(dnc));
+       pi.dn.bv_val = dnc;
+       pi.dn.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,svcc,sizeof(svcc));
+       pi.svc.bv_val = svcc;
+       pi.svc.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,pwdc,sizeof(pwdc));
+       pi.pwd.bv_val = pwdc;
+       pi.pwd.bv_len = tmpint32;
+
+       Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s)\n",pi.uid.bv_val,0,0);
+
+       rc = pam_do_bind(ni, fp, op, &pi);
+
+finish:
+       WRITE_INT32(fp,NSLCD_VERSION);
+       WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC);
+       WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
+       WRITE_BERVAL(fp,&pi.uid);
+       WRITE_BERVAL(fp,&pi.dn);
+       WRITE_INT32(fp,rc);
+       WRITE_INT32(fp,pi.authz);       /* authz */
+       WRITE_BERVAL(fp,&pi.msg);       /* authzmsg */
+       return 0;
+}
+
+static struct berval grpmsg =
+       BER_BVC("Access denied by group check");
+static struct berval hostmsg =
+       BER_BVC("Access denied for this host");
+static struct berval svcmsg =
+       BER_BVC("Access denied for this service");
+static struct berval uidmsg =
+       BER_BVC("Access denied by UID check");
+
+int pam_authz(nssov_info *ni,TFILE *fp,Operation *op)
+{
+       struct berval dn, uid, svc, ruser, rhost, tty;
+       struct berval authzmsg = BER_BVNULL;
+       int32_t tmpint32;
+       char dnc[1024];
+       char uidc[32];
+       char svcc[256];
+       char ruserc[32];
+       char rhostc[256];
+       char ttyc[256];
+       int rc = NSLCD_PAM_SUCCESS;
+       Entry *e = NULL;
+       Attribute *a;
+       SlapReply rs = {REP_RESULT};
+       slap_callback cb = {0};
+
+       READ_STRING_BUF2(fp,uidc,sizeof(uidc));
+       uid.bv_val = uidc;
+       uid.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,dnc,sizeof(dnc));
+       dn.bv_val = dnc;
+       dn.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,svcc,sizeof(svcc));
+       svc.bv_val = svcc;
+       svc.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,svcc,sizeof(ruserc));
+       ruser.bv_val = ruserc;
+       ruser.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,svcc,sizeof(rhostc));
+       rhost.bv_val = rhostc;
+       rhost.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,svcc,sizeof(ttyc));
+       tty.bv_val = ttyc;
+       tty.bv_len = tmpint32;
+
+       Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(%s)\n",dn.bv_val,0,0);
+
+       /* We don't do authorization if they weren't authenticated by us */
+       if (BER_BVISEMPTY(&dn)) {
+               rc = NSLCD_PAM_USER_UNKNOWN;
+               goto finish;
+       }
+
+       /* See if they have access to the host and service */
+       if ((ni->ni_pam_opts & NI_PAM_HOSTSVC) && nssov_pam_svc_ad) {
+               AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+               struct berval hostdn = BER_BVNULL;
+               struct berval odn = op->o_ndn;
+               op->o_dn = dn;
+               op->o_ndn = dn;
+               {
+                       nssov_mapinfo *mi = &ni->ni_maps[NM_host];
+                       char fbuf[1024];
+                       struct berval filter = {sizeof(fbuf),fbuf};
+                       SlapReply rs2 = {REP_RESULT};
+
+                       /* Lookup the host entry */
+                       nssov_filter_byname(mi,0,&global_host_bv,&filter);
+                       cb.sc_private = &hostdn;
+                       cb.sc_response = nssov_name2dn_cb;
+                       op->o_callback = &cb;
+                       op->o_req_dn = mi->mi_base;
+                       op->o_req_ndn = mi->mi_base;
+                       op->ors_scope = mi->mi_scope;
+                       op->ors_filterstr = filter;
+                       op->ors_filter = str2filter_x(op, filter.bv_val);
+                       op->ors_attrs = slap_anlist_no_attrs;
+                       op->ors_tlimit = SLAP_NO_LIMIT;
+                       op->ors_slimit = 2;
+                       rc = op->o_bd->be_search(op, &rs2);
+                       filter_free_x(op, op->ors_filter, 1);
+
+                       if (BER_BVISEMPTY(&hostdn) &&
+                               !BER_BVISEMPTY(&ni->ni_pam_defhost)) {
+                               filter.bv_len = sizeof(fbuf);
+                               filter.bv_val = fbuf;
+                               memset(&rs2, 0, sizeof(rs2));
+                               rs2.sr_type = REP_RESULT;
+                               nssov_filter_byname(mi,0,&ni->ni_pam_defhost,&filter);
+                               op->ors_filterstr = filter;
+                               op->ors_filter = str2filter_x(op, filter.bv_val);
+                               rc = op->o_bd->be_search(op, &rs2);
+                               filter_free_x(op, op->ors_filter, 1);
+                       }
+
+                       /* no host entry, no default host -> deny */
+                       if (BER_BVISEMPTY(&hostdn)) {
+                               rc = NSLCD_PAM_PERM_DENIED;
+                               authzmsg = hostmsg;
+                               goto finish;
+                       }
+               }
+
+               cb.sc_response = slap_null_cb;
+               cb.sc_private = NULL;
+               op->o_tag = LDAP_REQ_COMPARE;
+               op->o_req_dn = hostdn;
+               op->o_req_ndn = hostdn;
+               ava.aa_desc = nssov_pam_svc_ad;
+               ava.aa_value = svc;
+               op->orc_ava = &ava;
+               rc = op->o_bd->be_compare( op, &rs );
+               if ( rs.sr_err != LDAP_COMPARE_TRUE ) {
+                       authzmsg = svcmsg;
+                       rc = NSLCD_PAM_PERM_DENIED;
+                       goto finish;
+               }
+               op->o_dn = odn;
+               op->o_ndn = odn;
+       }
+
+       /* See if they're a member of the group */
+       if ((ni->ni_pam_opts & NI_PAM_USERGRP) &&
+               !BER_BVISEMPTY(&ni->ni_pam_group_dn) &&
+               ni->ni_pam_group_ad) {
+               AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+               op->o_callback = &cb;
+               cb.sc_response = slap_null_cb;
+               op->o_tag = LDAP_REQ_COMPARE;
+               op->o_req_dn = ni->ni_pam_group_dn;
+               op->o_req_ndn = ni->ni_pam_group_dn;
+               ava.aa_desc = ni->ni_pam_group_ad;
+               ava.aa_value = dn;
+               op->orc_ava = &ava;
+               rc = op->o_bd->be_compare( op, &rs );
+               if ( rs.sr_err != LDAP_COMPARE_TRUE ) {
+                       authzmsg = grpmsg;
+                       rc = NSLCD_PAM_PERM_DENIED;
+                       goto finish;
+               }
+       }
+
+       /* We need to check the user's entry for these bits */
+       if ((ni->ni_pam_opts & (NI_PAM_USERHOST|NI_PAM_USERSVC)) ||
+               ni->ni_pam_template_ad ||
+               ni->ni_pam_min_uid || ni->ni_pam_max_uid ) {
+               rc = be_entry_get_rw( op, &dn, NULL, NULL, 0, &e );
+               if (rc != LDAP_SUCCESS) {
+                       rc = NSLCD_PAM_USER_UNKNOWN;
+                       goto finish;
+               }
+       }
+       if ((ni->ni_pam_opts & NI_PAM_USERHOST) && nssov_pam_host_ad) {
+               a = attr_find(e->e_attrs, nssov_pam_host_ad);
+               if (!a || value_find_ex( nssov_pam_host_ad,
+                       SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
+                       a->a_vals, &global_host_bv, op->o_tmpmemctx )) {
+                       rc = NSLCD_PAM_PERM_DENIED;
+                       authzmsg = hostmsg;
+                       goto finish;
+               }
+       }
+       if ((ni->ni_pam_opts & NI_PAM_USERSVC) && nssov_pam_svc_ad) {
+               a = attr_find(e->e_attrs, nssov_pam_svc_ad);
+               if (!a || value_find_ex( nssov_pam_svc_ad,
+                       SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
+                       a->a_vals, &svc, op->o_tmpmemctx )) {
+                       rc = NSLCD_PAM_PERM_DENIED;
+                       authzmsg = svcmsg;
+                       goto finish;
+               }
+       }
+
+/* from passwd.c */
+#define UIDN_KEY       2
+
+       if (ni->ni_pam_min_uid || ni->ni_pam_max_uid) {
+               int id;
+               char *tmp;
+               nssov_mapinfo *mi = &ni->ni_maps[NM_host];
+               a = attr_find(e->e_attrs, mi->mi_attrs[UIDN_KEY].an_desc);
+               if (!a) {
+                       rc = NSLCD_PAM_PERM_DENIED;
+                       authzmsg = uidmsg;
+                       goto finish;
+               }
+               id = (int)strtol(a->a_vals[0].bv_val,&tmp,0);
+               if (a->a_vals[0].bv_val[0] == '\0' || *tmp != '\0') {
+                       rc = NSLCD_PAM_PERM_DENIED;
+                       authzmsg = uidmsg;
+                       goto finish;
+               }
+               if ((ni->ni_pam_min_uid && id < ni->ni_pam_min_uid) ||
+                       (ni->ni_pam_max_uid && id > ni->ni_pam_max_uid)) {
+                       rc = NSLCD_PAM_PERM_DENIED;
+                       authzmsg = uidmsg;
+                       goto finish;
+               }
+       }
+
+       if (ni->ni_pam_template_ad) {
+               a = attr_find(e->e_attrs, ni->ni_pam_template_ad);
+               if (a)
+                       uid = a->a_vals[0];
+               else if (!BER_BVISEMPTY(&ni->ni_pam_template))
+                       uid = ni->ni_pam_template;
+       }
+
+finish:
+       WRITE_INT32(fp,NSLCD_VERSION);
+       WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHZ);
+       WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
+       WRITE_BERVAL(fp,&uid);
+       WRITE_BERVAL(fp,&dn);
+       WRITE_INT32(fp,rc);
+       WRITE_BERVAL(fp,&authzmsg);
+       if (e) {
+               be_entry_release_r(op, e);
+       }
+       return 0;
+}
+
+static int pam_sess(nssov_info *ni,TFILE *fp,Operation *op,int action)
+{
+       struct berval dn, uid, svc, tty, rhost, ruser;
+       int32_t tmpint32;
+       char dnc[1024];
+       char svcc[256];
+       char uidc[32];
+       char ttyc[32];
+       char rhostc[256];
+       char ruserc[32];
+       slap_callback cb = {0};
+       SlapReply rs = {REP_RESULT};
+       char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
+       struct berval timestamp, bv[2], *nbv;
+       time_t stamp;
+       Modifications mod;
+
+       READ_STRING_BUF2(fp,uidc,sizeof(uidc));
+       uid.bv_val = uidc;
+       uid.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,dnc,sizeof(dnc));
+       dn.bv_val = dnc;
+       dn.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,svcc,sizeof(svcc));
+       svc.bv_val = svcc;
+       svc.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,ttyc,sizeof(ttyc));
+       tty.bv_val = ttyc;
+       tty.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,rhostc,sizeof(rhostc));
+       rhost.bv_val = rhostc;
+       rhost.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,ruserc,sizeof(ruserc));
+       ruser.bv_val = ruserc;
+       ruser.bv_len = tmpint32;
+       READ_INT32(fp,stamp);
+
+       Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(%s)\n",
+               action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', dn.bv_val,0);
+
+       if (!dn.bv_len || !ni->ni_pam_sessions) return 0;
+
+       {
+               int i, found=0;
+               for (i=0; !BER_BVISNULL(&ni->ni_pam_sessions[i]); i++) {
+                       if (ni->ni_pam_sessions[i].bv_len != svc.bv_len)
+                               continue;
+                       if (!strcasecmp(ni->ni_pam_sessions[i].bv_val, svc.bv_val)) {
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found) return 0;
+       }
+
+       slap_op_time( &op->o_time, &op->o_tincr );
+       timestamp.bv_len = sizeof(timebuf);
+       timestamp.bv_val = timebuf;
+       if (action == NSLCD_ACTION_PAM_SESS_O )
+               stamp = op->o_time;
+       slap_timestamp( &stamp, &timestamp );
+       bv[0].bv_len = timestamp.bv_len + global_host_bv.bv_len + svc.bv_len +
+               tty.bv_len + ruser.bv_len + rhost.bv_len + STRLENOF("    (@)");
+       bv[0].bv_val = op->o_tmpalloc( bv[0].bv_len+1, op->o_tmpmemctx );
+       sprintf(bv[0].bv_val, "%s %s %s %s (%s@%s)",
+               timestamp.bv_val, global_host_bv.bv_val, svc.bv_val, tty.bv_val,
+               ruser.bv_val, rhost.bv_val);
+       
+       mod.sml_numvals = 1;
+       mod.sml_values = bv;
+       BER_BVZERO(&bv[1]);
+       attr_normalize( ad_loginStatus, bv, &nbv, op->o_tmpmemctx );
+       mod.sml_nvalues = nbv;
+       mod.sml_desc = ad_loginStatus;
+       mod.sml_op = action == NSLCD_ACTION_PAM_SESS_O ? LDAP_MOD_ADD :
+               LDAP_MOD_DELETE;
+       mod.sml_flags = SLAP_MOD_INTERNAL;
+       mod.sml_next = NULL;
+
+       cb.sc_response = slap_null_cb;
+       op->o_callback = &cb;
+       op->o_tag = LDAP_REQ_MODIFY;
+       op->o_dn = op->o_bd->be_rootdn;
+       op->o_ndn = op->o_bd->be_rootndn;
+       op->orm_modlist = &mod;
+       op->orm_no_opattrs = 1;
+       op->o_req_dn = dn;
+       op->o_req_ndn = dn;
+       op->o_bd->be_modify( op, &rs );
+       if ( mod.sml_next ) {
+               slap_mods_free( mod.sml_next, 1 );
+       }
+       ber_bvarray_free_x( nbv, op->o_tmpmemctx );
+
+       WRITE_INT32(fp,NSLCD_VERSION);
+       WRITE_INT32(fp,action);
+       WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
+       WRITE_INT32(fp,op->o_time);
+       return 0;
+}
+
+int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op)
+{
+       return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_O);
+}
+
+int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op)
+{
+       return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_C);
+}
+
+int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op)
+{
+       struct berval npw;
+       int32_t tmpint32;
+       char dnc[1024];
+       char uidc[32];
+       char opwc[256];
+       char npwc[256];
+       char svcc[256];
+       struct paminfo pi;
+       int rc;
+
+       READ_STRING_BUF2(fp,uidc,sizeof(uidc));
+       pi.uid.bv_val = uidc;
+       pi.uid.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,dnc,sizeof(dnc));
+       pi.dn.bv_val = dnc;
+       pi.dn.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,svcc,sizeof(svcc));
+       pi.svc.bv_val = svcc;
+       pi.svc.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,opwc,sizeof(opwc));
+       pi.pwd.bv_val = opwc;
+       pi.pwd.bv_len = tmpint32;
+       READ_STRING_BUF2(fp,npwc,sizeof(npwc));
+       npw.bv_val = npwc;
+       npw.bv_len = tmpint32;
+
+       Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(%s), %s\n",
+               pi.dn.bv_val,pi.uid.bv_val,0);
+
+       BER_BVZERO(&pi.msg);
+
+       /* This is a prelim check */
+       if (BER_BVISEMPTY(&pi.dn)) {
+               rc = pam_do_bind(ni,fp,op,&pi);
+               if (rc == NSLCD_PAM_IGNORE)
+                       rc = NSLCD_PAM_SUCCESS;
+       } else {
+               BerElementBuffer berbuf;
+               BerElement *ber = (BerElement *)&berbuf;
+               struct berval bv;
+               SlapReply rs = {REP_RESULT};
+               slap_callback cb = {0};
+
+               ber_init_w_nullc(ber, LBER_USE_DER);
+               ber_printf(ber, "{");
+               if (!BER_BVISEMPTY(&pi.pwd))
+                       ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD,
+                               &pi.pwd);
+               if (!BER_BVISEMPTY(&npw))
+                       ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW,
+                               &npw);
+               ber_printf(ber, "N}");
+               ber_flatten2(ber, &bv, 0);
+               op->o_tag = LDAP_REQ_EXTENDED;
+               op->ore_reqoid = slap_EXOP_MODIFY_PASSWD;
+               op->ore_reqdata = &bv;
+               op->o_dn = pi.dn;
+               op->o_ndn = pi.dn;
+               op->o_callback = &cb;
+               op->o_conn->c_authz_backend = op->o_bd;
+               cb.sc_response = slap_null_cb;
+               op->o_bd = frontendDB;
+               rc = op->o_bd->be_extended(op, &rs);
+               if (rs.sr_text)
+                       ber_str2bv(rs.sr_text, 0, 0, &pi.msg);
+               if (rc == LDAP_SUCCESS)
+                       rc = NSLCD_PAM_SUCCESS;
+               else
+                       rc = NSLCD_PAM_PERM_DENIED;
+       }
+       WRITE_INT32(fp,NSLCD_VERSION);
+       WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD);
+       WRITE_INT32(fp,NSLCD_RESULT_SUCCESS);
+       WRITE_BERVAL(fp,&pi.uid);
+       WRITE_BERVAL(fp,&pi.dn);
+       WRITE_INT32(fp,rc);
+       WRITE_BERVAL(fp,&pi.msg);
+       return 0;
+}
+
+int nssov_pam_init()
+{
+       int code = 0;
+       if (!ad_loginStatus)
+               code = register_at( at_loginStatus, &ad_loginStatus, 0 );
+       return code;
+}
index 4897ac286b52cb00491d1af7184a4bf7e7e98a71..20ef4d67fbf3ee51e2b9dfeced31c3ffb321a555 100644 (file)
@@ -143,7 +143,7 @@ int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *u
        return 0;
 }
 
-static int uid2dn_cb(Operation *op,SlapReply *rs)
+int nssov_name2dn_cb(Operation *op,SlapReply *rs)
 {
        if ( rs->sr_type == REP_SEARCH )
        {
@@ -175,7 +175,7 @@ int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *
        nssov_filter_byid(mi,UID_KEY,uid,&filter);
        BER_BVZERO(dn);
        cb.sc_private = dn;
-       cb.sc_response = uid2dn_cb;
+       cb.sc_response = nssov_name2dn_cb;
        op2 = *op;
        op2.o_callback = &cb;
        op2.o_req_dn = mi->mi_base;
@@ -188,7 +188,7 @@ int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *
        op2.ors_slimit = SLAP_NO_LIMIT;
        rc = op2.o_bd->be_search( &op2, &rs );
        filter_free_x( op, op2.ors_filter, 1 );
-       return rc == LDAP_SUCCESS;
+       return rc == LDAP_SUCCESS && !BER_BVISNULL(dn);
 }
 
 /* the maximum number of uidNumber attributes per entry */
@@ -223,7 +223,7 @@ static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
                a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
                if (!a)
                {
-                       Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
+                       Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
                                entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,0);
                        return 0;
                }
@@ -264,7 +264,7 @@ static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
                a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc);
         if ( !a )
                {
-                       Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
+                       Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
                                entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,0);
                        return 0;
                }
@@ -280,19 +280,19 @@ static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
        a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc);
        if (!a)
        {
-               Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
                return 0;
        }
        else if (a->a_numvals != 1)
        {
-               Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values",
+               Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
        }
        gid=(gid_t)strtol(a->a_vals[0].bv_val,&tmp,0);
        if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0'))
        {
-               Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value",
+               Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
                return 0;
        }
@@ -302,7 +302,7 @@ static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
                a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
        if (!a || !a->a_numvals)
        {
-               Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value",
+               Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value\n",
                        entry->e_name.bv_val,
                        cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
                        cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
@@ -310,7 +310,7 @@ static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
        }
        else if (a->a_numvals > 1)
        {
-               Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values",
+               Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values\n",
                        entry->e_name.bv_val,
                        cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
                        cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
@@ -320,7 +320,7 @@ static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
        a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc);
        if (!a)
        {
-               Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0);
                homedir=default_passwd_homeDirectory;
        }
@@ -328,7 +328,7 @@ static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
        {
                if (a->a_numvals > 1)
                {
-                       Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values",
+                       Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
                                entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0);
                }
                homedir=a->a_vals[0];
@@ -345,7 +345,7 @@ static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
        {
                if (a->a_numvals > 1)
                {
-                       Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values",
+                       Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
                                entry->e_name.bv_val, cbp->mi->mi_attrs[SHL_KEY].an_desc->ad_cname.bv_val,0);
                }
                shell=a->a_vals[0];
@@ -357,7 +357,7 @@ static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
        {
                if (!isvalidusername(&names[i]))
                {
-                       Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"",
+                       Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"\n",
                                entry->e_name.bv_val,names[i].bv_val,0);
                }
                else
@@ -368,7 +368,7 @@ static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
                                uid_t uid;
                                uid = strtol(uids[j].bv_val, &tmp, 0);
                                if ( *tmp ) {
-                                       Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"",
+                                       Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"\n",
                                                entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,
                                                names[i].bv_val);
                                        continue;
@@ -398,11 +398,11 @@ NSSOV_HANDLE(
        cbp.name.bv_len = tmpint32;
        cbp.name.bv_val = cbp.buf;
        if (!isvalidusername(&cbp.name)) {
-               Debug(LDAP_DEBUG_ANY,"nssov_passwd_byname(%s): invalid user name",cbp.name.bv_val,0,0);
+               Debug(LDAP_DEBUG_ANY,"nssov_passwd_byname(%s): invalid user name\n",cbp.name.bv_val,0,0);
                return -1;
        }
        BER_BVZERO(&cbp.id); ,
-       Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)",cbp.name.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)\n",cbp.name.bv_val,0,0);,
        NSLCD_ACTION_PASSWD_BYNAME,
        nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
 )
@@ -417,7 +417,7 @@ NSSOV_HANDLE(
        cbp.id.bv_val = cbp.buf;
        cbp.id.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",uid);
        BER_BVZERO(&cbp.name);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byuid(%s)",cbp.id.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byuid(%s)\n",cbp.id.bv_val,0,0);,
        NSLCD_ACTION_PASSWD_BYUID,
        nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter)
 )
@@ -428,7 +428,7 @@ NSSOV_HANDLE(
        /* no parameters to read */
        BER_BVZERO(&cbp.name);
        BER_BVZERO(&cbp.id);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()",0,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()\n",0,0,0);,
        NSLCD_ACTION_PASSWD_ALL,
        (filter=cbp.mi->mi_filter,0)
 )
index 54a176bc2d2993a672a552ee9c22737b4fb2ce03..6e6e5b8b8b6661edd28534bf28dbad1650c28717 100644 (file)
@@ -59,7 +59,7 @@ static int write_protocol(nssov_protocol_cbp *cbp,Entry *entry)
        a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
        if ( !a || !a->a_vals )
        {
-               Debug(LDAP_DEBUG_ANY,"protocol entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"protocol entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val, 0 );
                return 0;
        }
@@ -82,17 +82,17 @@ static int write_protocol(nssov_protocol_cbp *cbp,Entry *entry)
        a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
        if ( !a || !a->a_vals )
        {
-               Debug(LDAP_DEBUG_ANY,"protocol entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"protocol entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
                return 0;
        } else if ( a->a_numvals > 1 ) {
-               Debug(LDAP_DEBUG_ANY,"protocol entry %s contains multiple %s values",
+               Debug(LDAP_DEBUG_ANY,"protocol entry %s contains multiple %s values\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
        }
        proto=(int)strtol(a->a_vals[0].bv_val,&tmp,0);
        if (*tmp)
        {
-               Debug(LDAP_DEBUG_ANY,"protocol entry %s contains non-numeric %s value",
+               Debug(LDAP_DEBUG_ANY,"protocol entry %s contains non-numeric %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
                return 0;
        }
@@ -123,7 +123,7 @@ NSSOV_HANDLE(
        READ_STRING_BUF2(fp,cbp.buf,sizeof(cbp.buf));
        cbp.name.bv_len = tmpint32;
        cbp.name.bv_val = cbp.buf;,
-       Debug(LDAP_DEBUG_TRACE,"nssov_protocol_byname(%s)",cbp.name.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_protocol_byname(%s)\n",cbp.name.bv_val,0,0);,
        NSLCD_ACTION_PROTOCOL_BYNAME,
        nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
 )
@@ -138,7 +138,7 @@ NSSOV_HANDLE(
        cbp.numb.bv_val = cbp.buf;
        cbp.numb.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",protocol);
        BER_BVZERO(&cbp.name);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_protocol_bynumber(%s)",cbp.numb.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_protocol_bynumber(%s)\n",cbp.numb.bv_val,0,0);,
        NSLCD_ACTION_PROTOCOL_BYNUMBER,
        nssov_filter_byid(cbp.mi,1,&cbp.numb,&filter)
 )
@@ -147,7 +147,7 @@ NSSOV_HANDLE(
        protocol,all,
        struct berval filter;
        /* no parameters to read */,
-       Debug(LDAP_DEBUG_TRACE,"nssov_protocol_all()",0,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_protocol_all()\n",0,0,0);,
        NSLCD_ACTION_PROTOCOL_ALL,
        (filter=cbp.mi->mi_filter,0)
 )
index b9e8ca887547342880f116290685706ade08835f..b86d913180328f7943446b764745361318208032 100644 (file)
@@ -62,7 +62,7 @@ static int write_rpc(nssov_rpc_cbp *cbp,Entry *entry)
        a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
        if ( !a || !a->a_vals )
        {
-               Debug(LDAP_DEBUG_ANY,"rpc entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"rpc entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val, 0 );
                return 0;
        }
@@ -85,17 +85,17 @@ static int write_rpc(nssov_rpc_cbp *cbp,Entry *entry)
        a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
        if ( !a || !a->a_vals )
        {
-               Debug(LDAP_DEBUG_ANY,"rpc entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"rpc entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
                return 0;
        } else if ( a->a_numvals > 1 ) {
-               Debug(LDAP_DEBUG_ANY,"rpc entry %s contains multiple %s values",
+               Debug(LDAP_DEBUG_ANY,"rpc entry %s contains multiple %s values\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
        }
        number=(int)strtol(a->a_vals[0].bv_val,&tmp,0);
        if (*tmp)
        {
-               Debug(LDAP_DEBUG_ANY,"rpc entry %s contains non-numeric %s value",
+               Debug(LDAP_DEBUG_ANY,"rpc entry %s contains non-numeric %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
                return 0;
        }
@@ -126,7 +126,7 @@ NSSOV_HANDLE(
     READ_STRING_BUF2(fp,cbp.buf,sizeof(cbp.buf));
     cbp.name.bv_len = tmpint32;
     cbp.name.bv_val = cbp.buf;,
-       Debug(LDAP_DEBUG_TRACE,"nssov_rpc_byname(%s)",cbp.name.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_rpc_byname(%s)\n",cbp.name.bv_val,0,0);,
        NSLCD_ACTION_RPC_BYNAME,
        nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
 )
@@ -141,7 +141,7 @@ NSSOV_HANDLE(
        cbp.numb.bv_val = cbp.buf;
        cbp.numb.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",number);
        BER_BVZERO(&cbp.name);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_rpc_bynumber(%d)",cbp.numb.bv_val,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_rpc_bynumber(%s)\n",cbp.numb.bv_val,0,0);,
        NSLCD_ACTION_RPC_BYNUMBER,
        nssov_filter_byid(cbp.mi,1,&cbp.numb,&filter)
 )
@@ -150,7 +150,7 @@ NSSOV_HANDLE(
        rpc,all,
        struct berval filter;
        /* no parameters to read */,
-       Debug(LDAP_DEBUG_TRACE,"nssov_rpc_all()",0,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_rpc_all()\n",0,0,0);,
        NSLCD_ACTION_RPC_ALL,
        (filter=cbp.mi->mi_filter,0)
 )
index ceaf901187a91e85f19610f64428c4648b268b7e..ac5390a18095854463e1cc226886a447ddb2b43c 100644 (file)
@@ -124,7 +124,7 @@ static int write_service(nssov_service_cbp *cbp,Entry *entry)
        a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
        if ( !a || !a->a_vals )
        {
-               Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val, 0 );
                return 0;
        }
@@ -147,17 +147,17 @@ static int write_service(nssov_service_cbp *cbp,Entry *entry)
        a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
        if ( !a || !a->a_vals )
        {
-               Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value",
+               Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
                return 0;
        } else if ( a->a_numvals > 1 ) {
-               Debug(LDAP_DEBUG_ANY,"service entry %s contains multiple %s values",
+               Debug(LDAP_DEBUG_ANY,"service entry %s contains multiple %s values\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
        }
        port=(int)strtol(a->a_vals[0].bv_val,&tmp,0);
        if (*tmp)
        {
-               Debug(LDAP_DEBUG_ANY,"service entry %s contains non-numeric %s value",
+               Debug(LDAP_DEBUG_ANY,"service entry %s contains non-numeric %s value\n",
                        entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 );
                return 0;
        }
@@ -167,7 +167,7 @@ static int write_service(nssov_service_cbp *cbp,Entry *entry)
                a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[2].an_desc );
                if ( !a || !a->a_vals )
                {
-                       Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value",
+                       Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value\n",
                                entry->e_name.bv_val, cbp->mi->mi_attrs[2].an_desc->ad_cname.bv_val, 0 );
                        return 0;
                }
@@ -215,7 +215,7 @@ NSSOV_HANDLE(
        READ_STRING_BUF2(fp,cbp.pbuf,sizeof(cbp.pbuf));
        cbp.prot.bv_len = tmpint32;
        cbp.prot.bv_val = tmpint32 ? cbp.pbuf : NULL;,
-       Debug(LDAP_DEBUG_TRACE,"nssov_service_byname(%s,%s)",cbp.name.bv_val,cbp.prot.bv_val,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_service_byname(%s,%s)\n",cbp.name.bv_val,cbp.prot.bv_val,0);,
        NSLCD_ACTION_SERVICE_BYNAME,
        mkfilter_service_byname(cbp.mi,&cbp.name,&cbp.prot,&filter)
 )
@@ -232,7 +232,7 @@ NSSOV_HANDLE(
        READ_STRING_BUF2(fp,cbp.pbuf,sizeof(cbp.pbuf));
        cbp.prot.bv_len = tmpint32;
        cbp.prot.bv_val = tmpint32 ? cbp.pbuf : NULL;,
-       Debug(LDAP_DEBUG_TRACE,"nssov_service_bynumber(%s,%s)",cbp.name.bv_val,cbp.prot.bv_val,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_service_bynumber(%s,%s)\n",cbp.name.bv_val,cbp.prot.bv_val,0);,
        NSLCD_ACTION_SERVICE_BYNUMBER,
        mkfilter_service_bynumber(cbp.mi,&cbp.name,&cbp.prot,&filter)
 )
@@ -242,7 +242,7 @@ NSSOV_HANDLE(
        struct berval filter;
        /* no parameters to read */
        BER_BVZERO(&cbp.prot);,
-       Debug(LDAP_DEBUG_TRACE,"nssov_service_all()",0,0,0);,
+       Debug(LDAP_DEBUG_TRACE,"nssov_service_all()\n",0,0,0);,
        NSLCD_ACTION_SERVICE_ALL,
        (filter=cbp.mi->mi_filter,0)
 )
index 49fd6fbd3f6b4a84a66552859b37a5068fb4ce69..f331e59cd198c428e39887106a39d9bd466dcc54 100644 (file)
@@ -91,7 +91,7 @@ static long to_date(struct berval *date,AttributeDescription *attr)
                value=strtol(buffer,&tmp,0);
                if ((buffer[0]=='\0')||(*tmp!='\0'))
                {
-                       Debug(LDAP_DEBUG_ANY,"shadow entry contains non-numeric %s value",
+                       Debug(LDAP_DEBUG_ANY,"shadow entry contains non-numeric %s value\n",
                                attr->ad_cname.bv_val,0,0);
                        return 0;
                }
@@ -102,7 +102,7 @@ static long to_date(struct berval *date,AttributeDescription *attr)
        value=strtol(date->bv_val,&tmp,0);
        if ((date->bv_val[0]=='\0')||(*tmp!='\0'))
        {
-               Debug(LDAP_DEBUG_ANY,"shadow entry contains non-numeric %s value",
+               Debug(LDAP_DEBUG_ANY,"shadow entry contains non-numeric %s value\n",
                        attr->ad_cname.bv_val,0,0);
                return 0;
        }
@@ -121,13 +121,13 @@ static long to_date(struct berval *date,AttributeDescription *attr)
        { \
                if (a->a_numvals > 1) \
                { \
-                       Debug(LDAP_DEBUG_ANY,"shadow entry %s contains multiple %s values", \
+                       Debug(LDAP_DEBUG_ANY,"shadow entry %s contains multiple %s values\n", \
                                entry->e_name.bv_val, cbp->mi->mi_attrs[key].an_desc->ad_cname.bv_val,0); \
                } \
                var=strtol(a->a_vals[0].bv_val,&tmp,0); \
                if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0')) \
                { \
-                       Debug(LDAP_DEBUG_ANY,"shadow entry %s contains non-numeric %s value", \
+                       Debug(LDAP_DEBUG_ANY,"shadow entry %s contains non-numeric %s value\n", \
                                entry->e_name.bv_val, cbp->mi->mi_attrs[key].an_desc->ad_cname.bv_val,0); \
                        return 0; \
                } \
@@ -141,7 +141,7 @@ static long to_date(struct berval *date,AttributeDescription *attr)
        { \
                if (a->a_numvals > 1) \
                { \
-                       Debug(LDAP_DEBUG_ANY,"shadow entry %s contains multiple %s values", \
+                       Debug(LDAP_DEBUG_ANY,"shadow entry %s contains multiple %s values\n", \
                                entry->e_name.bv_val, cbp->mi->mi_attrs[key].an_desc->ad_cname.bv_val,0); \
                } \
                var=to_date(&a->a_vals[0],cbp->mi->mi_attrs[key].an_desc); \
@@ -173,7 +173,7 @@ static int write_shadow(nssov_shadow_cbp *cbp,Entry *entry)
                a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
                if (!a)
                {
-                       Debug(LDAP_DEBUG_ANY,"shadow entry %s does not contain %s value",
+                       Debug(LDAP_DEBUG_ANY,"shadow entry %s does not contain %s value\n",
                                entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,0);
                        return 0;
                }
@@ -239,7 +239,7 @@ NSSOV_HANDLE(
        READ_STRING_BUF2(fp,cbp.buf,sizeof(cbp.buf));,
        cbp.name.bv_len = tmpint32;
        cbp.name.bv_val = cbp.buf;
-       Debug(LDAP_DEBUG_ANY,"nssov_shadow_byname(%s)",cbp.name.bv_val,0,0);,
+       Debug(LDAP_DEBUG_ANY,"nssov_shadow_byname(%s)\n",cbp.name.bv_val,0,0);,
        NSLCD_ACTION_SHADOW_BYNAME,
        nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
 )
@@ -249,7 +249,7 @@ NSSOV_HANDLE(
        struct berval filter;
        /* no parameters to read */
        BER_BVZERO(&cbp.name);,
-       Debug(LDAP_DEBUG_ANY,"nssov_shadow_all()",0,0,0);,
+       Debug(LDAP_DEBUG_ANY,"nssov_shadow_all()\n",0,0,0);,
        NSLCD_ACTION_SHADOW_ALL,
        (filter=cbp.mi->mi_filter,0)
 )
diff --git a/contrib/slapd-modules/nssov/slapo-nssov.5 b/contrib/slapd-modules/nssov/slapo-nssov.5
new file mode 100644 (file)
index 0000000..7e45d77
--- /dev/null
@@ -0,0 +1,299 @@
+.TH SLAPO-NSSOV 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2009 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply.  See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo-nssov \- NSS and PAM requests through a local Unix Domain socket
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B nssov 
+overlay to
+.BR slapd (8)
+services NSS and PAM requests through a local Unix Domain socket. 
+It uses the same IPC protocol as Arthur de Jong's nss-ldapd, and 
+a complete copy of the nss-ldapd source is included along with the
+nssov source code.
+.LP
+Using a separate IPC protocol for NSS and PAM requests eliminates the
+libldap dependencies/clashes that the current pam_ldap/nss_ldap solutions
+all suffer from. Both the original nss-ldapd and this nssov solution
+are free from these library issues.
+.LP
+Unlike nss-ldapd, since this overlay executes inside slapd it allows for
+the possibility of sophisticated caching, without any of the weaknesses of
+nscd and other related caching solutions. E.g., a remote LDAP database can
+be accessed using back-ldap with proxy caching (see
+.BR slapd-ldap (5)
+and
+.BR slapo-pcache (5)
+) to leverage back-ldap's
+connection pooling as well as pcache's persistent caching, to provide
+high performance and a measure of support for disconnected operation.
+Alternatively, cache considerations can be completely eliminated by running
+a regular database with syncrepl to maintain synchronization with a remote
+LDAP database.
+.LP
+Another major benefit of nssov is that it allows all security policy to be 
+administered centrally via LDAP, instead of having fragile rules scattered 
+across multiple flat files. As such, there is no client-side configuration at 
+all for the NSS/PAM stub libraries. (The stubs talk to the server via a Unix
+domain socket whose path is hardcoded to /var/run/nslcd/). As a side benefit,
+this can finally eliminate the perpetual confusion between OpenLDAP's
+ldap.conf file in ETCDIR/ldap.conf and the similarly named files typically
+used by pam_ldap and nss_ldap.
+.LP
+User authentication is performed by internal simple Binds. User authorization 
+leverages the slapd ACL engine, which offers much more power and flexibility 
+than the simple group/hostname checks in the old pam_ldap code.
+.LP
+To use this code, you will need the client-side stub library from
+nss-ldapd (which resides in nss-ldapd/nss). You will not need the
+nslcd daemon; this overlay replaces that part. You should already
+be familiar with the [RFC2307] and [RFC2307bis] schema to use this
+overlay. See the 
+.B nss-ldapd/README 
+for more information on the schema and which features are supported.
+.LP
+You will also need to include the nis.schema in your slapd configuration
+for RFC2307 support. If you wish to use RFC2307bis you will need a slightly
+different schema. You will also need the ldapns.schema for PAM authorization
+management.
+.LP
+You must select
+.B ldap
+in the appropriate services in
+.I /etc/nsswitch.conf
+in order for these NSS features to take effect. Likewise, you must
+enable
+.B pam_ldap
+for the authenticate, account, session, and password services in
+.I /etc/pam.conf
+or
+.I /etc/pam.d
+for these PAM features to take effect.
+
+.TP
+.B overlay nssov
+This directive adds the nssov overlay to the current backend.
+.TP
+.B nssov-ssd <service> <url>
+This directive configures a Service Search Descriptor (SSD) for each NSS
+service that will be used.  The <service> may be one of
+.RS
+.nf
+    alias
+    ether
+    group
+    host
+    netgroup
+    network
+    passwd
+    protocol
+    rpc
+    service
+    shadow
+.fi
+.RE
+and the <url> must be of the form
+.RS
+.TP
+.B ldap:///[<basedn>][??[<scope>][?<filter>]]
+.RE
+The 
+.B <basedn> 
+will default to the first suffix of the current database.
+The 
+.B <scope> 
+defaults to "subtree". The default 
+.B <filter> 
+depends on which service is being used.
+.TP
+.B nssov-map <service> <orig> <new>
+If the local database is actually a proxy to a foreign LDAP server, some
+mapping of schema may be needed. This directive allows some simple attribute
+substitutions to be performed. See the 
+.B nss-ldapd/README 
+for the original attribute names used in this code.
+.TP
+.B nssov-pam <option> [...]
+This directive determines a number of PAM behaviors. Multiple options may
+be used at once, and available levels are:
+.RS
+.RS
+.PD 0
+.TP
+.B userhost
+check host attribute in user entry for authorization
+.TP
+.B userservice
+check authorizedService attribute in user entry for authorization
+.TP
+.B usergroup
+check that user is a member of specific group for authorization
+.TP
+.B hostservice
+check authorizedService attribute in host entry for authorization
+.TP
+.B authz2dn
+use authz-regexp mapping to map uid to LDAP DN
+.TP
+.B uid2dn
+use NSS passwd SSD to map uid to LDAP DN
+.PD
+.RE
+
+Setting the
+.BR userhost ,
+.BR userservice ,
+and
+.B usergroup
+options duplicates the original pam_ldap authorization behavior.
+
+The recommended approach is to use
+.B hostservice
+instead. In this case, ipHost entries must be created for all hosts
+being managed, and they must also have the authorizedServiceObject
+class to allow authorizedService attributes to be used. Also the
+NSS host SSD must be configured so that ipHost entries can be found.
+Authorization is checked by performing an LDAP Compare operation
+looking for the PAM service name in the authorizedService attribute.
+.B slapd
+ACLs should be set to grant or deny
+.B Compare
+privilege to the appropriate users or groups as desired.
+
+If the
+.B authz2dn
+option is set then authz-regexp mappings will be used to map the
+PAM username to an LDAP DN. The authentication DN will be of the
+form
+.RS
+.B cn=<service>+uid=<user>,cn=<hostname>,cn=pam,cn=auth
+.RE
+
+If no mapping is found for this authentication DN, then this
+mapping will be ignored.
+
+If the
+.B uid2dn
+option is set then the NSS passwd SSD will be used to map the
+PAM username to an LDAP DN. The passwd SSD must have already been
+configured for this mapping to succeed.
+
+If neither the authz2dn nor the uid2dn mapping succeeds, the module
+will return a PAM_USER_UNKNOWN failure code. If both options are set,
+the authz mapping is attempted first; if it succeeds the uid2dn mapping
+will be skipped.
+
+By default only the
+.B uid2dn
+option is set.
+.RE
+.TP
+.B nssov-pam-defhost <hostname>
+Specify a default hostname to check if an ipHost entry for the current
+hostname cannot be found. This setting is only relevant if the 
+.B hostservice
+option has been set.
+.TP
+.B nssov-pam-group-dn <DN>
+Specify the DN of an LDAP group to check for authorization. The LDAP user
+must be a member of this group for the login to be allowed. There is no
+default value. This setting is only relevant if the
+.B usergroup
+option has been set.
+.TP
+.B nssov-pam-group-ad <attribute>
+Specify the attribute to use for group membership checks.
+There is no default value.  This setting is only relevant if the
+.B usergroup
+option has been set.
+.TP
+.B nssov-pam-minuid <integer>
+Specify a minimum uid that is allowed to login. Users with a uidNumber
+lower than this value will be denied access. The default is zero, which
+disables this setting.
+.TP
+.B nssov-pam-maxuid <integer>
+Specify a maximum uid that is allowed to login. Users with a uidNumber
+higher than this value will be denied access. The default is zero, which
+disables this setting.
+.TP
+.B nssov-pam-template-ad <attribute>
+Specify an attribute to check in a user's entry for a template login name.
+The template login feature is used by FreeBSD's PAM framework. It can be
+viewed as a form of proxying, where a user can authenticate with one
+username/password pair, but is assigned the identity and credentials of
+the template user. This setting is disabled by default.
+.TP
+.B nssov-pam-template <name>
+Specify a default username to be used if no template attribute is found
+in the user's entry. The
+.B nssov-pam-template-ad
+directive must be configured for this setting to have any effect.
+.TP
+.B nssov-pam-session <service>
+Specify a PAM service name whose sessions will be recorded. For the
+configured services, logins will be recorded in the
+.B loginStatus
+operational attribute of the user's entry. The attribute's values are
+of the form
+.RS
+.RS
+.B <generalizedTime> <host> <service> <tty> (<ruser@rhost>)
+.RE
+.RE
+Upon logout the corresponding value will be deleted. This feature allows
+a single LDAP Search to be used to check which users are logged in across
+all the hosts of a network. By default no services are configured.
+.LP
+The PAM functions support LDAP Password Policy as well. If the password
+policy overlay is in use (see
+.BR slapo-ppolicy (5)),
+policy
+information (e.g. password expiration, password quality, etc.)
+may be returned to the PAM client as a result of authentication,
+account management, and password modification requests.
+
+The overlay also supports dynamic configuration in cn=config. An example
+of the config entry is
+.LP 
+.RS
+.nf
+    dn: olcOverlay={0}nssov,ocDatabase={1}hdb,cn=config
+    objectClass: olcOverlayConfig
+    objectClass: olcNssOvConfig
+    olcOverlay: {0}nssov
+    olcNssSsd: passwd ldap:///ou=users,dc=example,dc=com??one
+    olcNssMap: passwd uid accountName
+    olcNssPam: hostservice uid2dn
+    olcNssPamDefHost: defaulthost
+    olcNssPamMinUid: 500
+    olcNssPamMaxUid: 32000
+    olcNssPamSession: login
+    olcNssPamSession: sshd
+.fi
+.RE
+.LP
+which enables the passwd service, and uses the accountName attribute to
+fetch what is usually retrieved from the uid attribute. It also enables
+some PAM authorization controls, and specifies that the PAM
+.B login
+and
+.B sshd
+services should have their logins recorded.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd\-ldap (5),
+.BR slapo\-pcache (5),
+.BR slapo\-ppolicy (5),
+.BR slapd (8).
+.SH AUTHOR
+Howard Chu, inspired by nss-ldapd by Arthur de Jong and pam_ldap by Luke Howard