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
-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
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
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
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.
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;
}
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)
)
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)
)
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;
}
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;
}
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)
)
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)
)
/* 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)
)
/* 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
/* 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') ||
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;
}
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;
}
{
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
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;
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)
)
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)
)
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)
)
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)
)
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;
}
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;
}
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)
)
/* 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)
)
/* 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)
)
--- /dev/null
+# $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 )
/* 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++;
/* 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;
/* 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;
/* 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;
/* 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 */
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)
)
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;
}
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;
}
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)
)
/* 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)
)
/* 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)
)
#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 */
../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
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) \
../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
@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@
_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:
*;
--- /dev/null
+/*
+ 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 */
/* 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
#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
/* 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 */
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;
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 */
/* 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 */
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;
}
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;
{ 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;
"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 }
};
"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 }
};
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) {
}
}
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 ) {
}
}
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;
}
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 */
nssov_shadow_init(ni);
ni->ni_db = be->bd_self;
+ ni->ni_pam_opts = NI_PAM_UID2DN;
return 0;
}
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 */
#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>
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);
/* 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);
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);
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) \
--- /dev/null
+/* 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, ×tamp );
+ 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;
+}
return 0;
}
-static int uid2dn_cb(Operation *op,SlapReply *rs)
+int nssov_name2dn_cb(Operation *op,SlapReply *rs)
{
if ( rs->sr_type == REP_SEARCH )
{
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;
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 */
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;
}
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;
}
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;
}
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);
}
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);
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;
}
{
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];
{
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];
{
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
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;
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)
)
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)
)
/* 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)
)
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;
}
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;
}
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)
)
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)
)
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)
)
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;
}
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;
}
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)
)
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)
)
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)
)
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;
}
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;
}
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;
}
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)
)
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)
)
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)
)
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;
}
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;
}
{ \
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; \
} \
{ \
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); \
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;
}
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)
)
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)
)
--- /dev/null
+.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