diff options
Diffstat (limited to 'contrib/slapd-modules/nssov')
24 files changed, 6566 insertions, 0 deletions
diff --git a/contrib/slapd-modules/nssov/Makefile b/contrib/slapd-modules/nssov/Makefile new file mode 100644 index 0000000..f22b0e9 --- /dev/null +++ b/contrib/slapd-modules/nssov/Makefile @@ -0,0 +1,73 @@ +# $OpenLDAP$ +# This work is part of OpenLDAP Software <http://www.openldap.org/>. +# +# Copyright 2008-2018 The OpenLDAP Foundation. +# Portions Copyright 2008 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>. + +# Path to the OpenLDAP source tree +LDAP_SRC=../../.. + +# Path to the OpenLDAP object tree - same as above unless +# you're doing out-of-tree builds. +LDAP_BUILD=$(LDAP_SRC) + +LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd +LDAP_LIB = $(LDAP_BUILD)/libraries/libldap_r/libldap_r.la \ + $(LDAP_BUILD)/libraries/liblber/liblber.la + +NLDAPD_INC=-Inss-pam-ldapd + +LIBTOOL = $(LDAP_BUILD)/libtool +OPT = -g -O2 -Wall +CC = gcc +DEFS = +INCS = $(LDAP_INC) $(NLDAPD_INC) +LIBS = $(LDAP_LIB) + +prefix=/usr/local +exec_prefix=$(prefix) +ldap_subdir=/openldap + +libdir=$(exec_prefix)/lib +libexecdir=$(exec_prefix)/libexec +moduledir = $(libexecdir)$(ldap_subdir) +sysconfdir = $(prefix)/etc$(ldap_subdir) +schemadir = $(sysconfdir)/schema + +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 pam.lo + +.SUFFIXES: .c .o .lo + +.c.lo: + $(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $< + +tio.lo: nss-pam-ldapd/tio.c + $(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $? + +$(OBJS): nssov.h + +nssov.la: $(OBJS) $(XOBJS) + $(LIBTOOL) --mode=link $(CC) $(OPT) -version-info 0:0:0 \ + -rpath $(moduledir) -module -o $@ $(OBJS) $(XOBJS) $(LIBS) + +install: nssov.la + mkdir -p $(DESTDIR)$(moduledir) + $(LIBTOOL) --mode=install cp nssov.la $(DESTDIR)$(moduledir) + cp ldapns.schema $(DESTDIR)$(schemadir) + +clean: + rm -f *.*o *.la .libs/* + rm -rf .libs diff --git a/contrib/slapd-modules/nssov/README b/contrib/slapd-modules/nssov/README new file mode 100644 index 0000000..1462c4d --- /dev/null +++ b/contrib/slapd-modules/nssov/README @@ -0,0 +1,134 @@ +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. It also handles +PAM requests. + +To use this code, you will need the client-side stuf library from +nss-pam-ldapd. You can get it from: +http://arthurdejong.org/nss-pam-ldapd +You will not need the nslcd daemon; this overlay replaces that part. +To disable building of the nslcd daemon in nss-pam-ldapd, add the +--disable-nslcd option to the nss-pam-ldapd configure script. You +should already be familiar with the RFC2307 and RFC2307bis schema +to use this overlay. See the nss-pam-ldapd README for more information +on the schema and which features are supported. + +To use the overlay, add: + + include <path to>nis.schema + + moduleload <path to>nssov.so + ... + + database hdb + ... + overlay nssov + +to your slapd configuration file. (The nis.schema file contains +the original RFC2307 schema. Some modifications will be needed to +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-ssd <service> <url> + +where the <service> may be one of + aliases + ethers + group + hosts + netgroup + networks + passwd + protocols + rpc + services + shadow + +and the <url> must be of the form + ldap:///[<basedn>][??[<scope>][?<filter>]] + +The <basedn> will default to the first suffix of the current database. +The <scope> defaults to "subtree". The default <filter> depends on which +service is being used. + +If the local database is actually a proxy to a foreign LDAP server, some +mapping of schema may be needed. Some simple attribute substitutions may +be performed using + + nssov-map <service> <orig> <new> + +See the nss-ldapd/README for the original attribute names used in this code. + +The overlay also supports dynamic configuration in cn=config. The layout +of the config entry is + + 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 + +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. + +--- +This work is part of OpenLDAP Software <http://www.openldap.org/>. + +Copyright 1998-2018 The OpenLDAP Foundation. +Portions 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 +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>. + diff --git a/contrib/slapd-modules/nssov/alias.c b/contrib/slapd-modules/nssov/alias.c new file mode 100644 index 0000000..998093b --- /dev/null +++ b/contrib/slapd-modules/nssov/alias.c @@ -0,0 +1,116 @@ +/* alias.c - mail alias lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +/* Vendor-specific attributes and object classes. + * (Mainly from Sun.) + * ( 1.3.6.1.4.1.42.2.27.1.2.5 NAME 'nisMailAlias' SUP top STRUCTURAL + * DESC 'NIS mail alias' + * MUST cn + * MAY rfc822MailMember ) + */ + +/* the basic search filter for searches */ +static struct berval alias_filter = BER_BVC("(objectClass=nisMailAlias)"); + +/* the attributes to request with searches */ +static struct berval alias_keys[] = { + BER_BVC("cn"), + BER_BVC("rfc822MailMember"), + BER_BVNULL +}; + +NSSOV_INIT(alias) + +NSSOV_CBPRIV(alias, + struct berval name; + char buf[256];); + +static int write_alias(nssov_alias_cbp *cbp,Entry *entry) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + struct berval tmparr[2], empty; + struct berval *names, *members; + Attribute *a; + int i; + + /* get the name of the alias */ + if (BER_BVISNULL(&cbp->name)) + { + 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\n", + entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val,0 ); + return 0; + } + names = a->a_vals; + } + else + { + names=tmparr; + names[0]=cbp->name; + BER_BVZERO(&names[1]); + } + /* get the members of the alias */ + a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[1].an_desc); + if ( !a ) { + BER_BVZERO( &empty ); + members = ∅ + } else { + members = a->a_vals; + } + /* for each name, write an entry */ + for (i=0;!BER_BVISNULL(&names[i]);i++) + { + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + WRITE_BERVAL(cbp->fp,&names[i]); + WRITE_BVARRAY(cbp->fp,members); + } + return 0; +} + +NSSOV_CB(alias) + +NSSOV_HANDLE( + alias,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_STRING(fp,cbp.buf); + cbp.name.bv_len = tmpint32; + cbp.name.bv_val = cbp.buf;, + 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) +) + +NSSOV_HANDLE( + alias,all, + struct berval filter; + /* no parameters to read */ + BER_BVZERO(&cbp.name);, + Debug(LDAP_DEBUG,"nssov_alias_all()\n",0,0,0);, + NSLCD_ACTION_ALIAS_ALL, + (filter=cbp.mi->mi_filter,0) +) diff --git a/contrib/slapd-modules/nssov/ether.c b/contrib/slapd-modules/nssov/ether.c new file mode 100644 index 0000000..1e7e6b1 --- /dev/null +++ b/contrib/slapd-modules/nssov/ether.c @@ -0,0 +1,167 @@ +/* ether.c - ethernet address lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Copyright 2008 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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +struct ether_addr { + uint8_t ether_addr_octet[6]; +}; + +/* ( nisSchema.2.11 NAME 'ieee802Device' SUP top AUXILIARY + * DESC 'A device with a MAC address; device SHOULD be + * used as a structural class' + * MAY macAddress ) + */ + +/* the basic search filter for searches */ +static struct berval ether_filter = BER_BVC("(objectClass=ieee802Device)"); + +/* the attributes to request with searches */ +static struct berval ether_keys[] = { + BER_BVC("cn"), + BER_BVC("macAddress"), + BER_BVNULL +}; + +NSSOV_INIT(ether) + +NSSOV_CBPRIV(ether, + char buf[256]; + struct berval name; + struct berval addr;); + +#define WRITE_ETHER(fp,addr) \ + {int ao[6]; \ + sscanf(addr.bv_val,"%02x:%02x:%02x:%02x:%02x:%02x", \ + &ao[0], &ao[1], &ao[2], &ao[3], &ao[4], &ao[5] );\ + tmpaddr.ether_addr_octet[0] = ao[0]; \ + tmpaddr.ether_addr_octet[1] = ao[1]; \ + tmpaddr.ether_addr_octet[2] = ao[2]; \ + tmpaddr.ether_addr_octet[3] = ao[3]; \ + tmpaddr.ether_addr_octet[4] = ao[4]; \ + tmpaddr.ether_addr_octet[5] = ao[5]; } \ + WRITE(fp,&tmpaddr,sizeof(uint8_t[6])); + +static int write_ether(nssov_ether_cbp *cbp,Entry *entry) +{ + int32_t tmpint32; + struct ether_addr tmpaddr; + struct berval tmparr[2]; + struct berval *names,*ethers; + Attribute *a; + int i,j; + + /* get the name of the ether entry */ + if (BER_BVISNULL(&cbp->name)) + { + 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\n", + entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val,0 ); + return 0; + } + names = a->a_vals; + } + else + { + names=tmparr; + names[0]=cbp->name; + BER_BVZERO(&names[1]); + } + /* get the addresses */ + if (BER_BVISNULL(&cbp->addr)) + { + 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\n", + entry->e_name.bv_val,cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val,0 ); + return 0; + } + ethers = a->a_vals; + /* TODO: move parsing of addresses up here */ + } + else + { + ethers=tmparr; + ethers[0]=cbp->addr; + BER_BVZERO(ðers[1]); + } + /* write entries for all names and addresses */ + for (i=0;!BER_BVISNULL(&names[i]);i++) + for (j=0;!BER_BVISNULL(ðers[j]);j++) + { + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + WRITE_BERVAL(cbp->fp,&names[i]); + WRITE_ETHER(cbp->fp,ethers[j]); + } + return 0; +} + +NSSOV_CB(ether) + +NSSOV_HANDLE( + ether,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + BER_BVZERO(&cbp.addr); + READ_STRING(fp,cbp.buf); + cbp.name.bv_len = tmpint32; + cbp.name.bv_val = cbp.buf;, + 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) +) + +NSSOV_HANDLE( + ether,byether, + struct ether_addr addr; + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + BER_BVZERO(&cbp.name); + READ(fp,&addr,sizeof(uint8_t[6])); + cbp.addr.bv_len = snprintf(cbp.buf,sizeof(cbp.buf), "%x:%x:%x:%x:%x:%x", + addr.ether_addr_octet[0], + addr.ether_addr_octet[1], + addr.ether_addr_octet[2], + addr.ether_addr_octet[3], + addr.ether_addr_octet[4], + addr.ether_addr_octet[5]); + cbp.addr.bv_val = cbp.buf;, + 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) +) + +NSSOV_HANDLE( + ether,all, + struct berval filter; + /* no parameters to read */ + BER_BVZERO(&cbp.name); + BER_BVZERO(&cbp.addr);, + Debug(LDAP_DEBUG_TRACE,"nssov_ether_all()\n",0,0,0);, + NSLCD_ACTION_ETHER_ALL, + (filter=cbp.mi->mi_filter,0) +) diff --git a/contrib/slapd-modules/nssov/group.c b/contrib/slapd-modules/nssov/group.c new file mode 100644 index 0000000..3452357 --- /dev/null +++ b/contrib/slapd-modules/nssov/group.c @@ -0,0 +1,346 @@ +/* group.c - group lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008-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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +/* for gid_t */ +#include <grp.h> + +/* ( nisSchema.2.2 NAME 'posixGroup' SUP top STRUCTURAL + * DESC 'Abstraction of a group of accounts' + * MUST ( cn $ gidNumber ) + * MAY ( userPassword $ memberUid $ description ) ) + * + * apart from that the above the uniqueMember attributes may be + * supported in a coming release (they map to DNs, which is an extra + * lookup step) + * + * using nested groups (groups that are member of a group) is currently + * not supported, this may be added in a later release + */ + +/* the basic search filter for searches */ +static struct berval group_filter = BER_BVC("(objectClass=posixGroup)"); + +/* the attributes to request with searches */ +static struct berval group_keys[] = { + BER_BVC("cn"), + BER_BVC("userPassword"), + BER_BVC("gidNumber"), + BER_BVC("memberUid"), + BER_BVC("uniqueMember"), + BER_BVNULL +}; + +#define CN_KEY 0 +#define PWD_KEY 1 +#define GID_KEY 2 +#define UID_KEY 3 +#define MEM_KEY 4 + +/* default values for attributes */ +static struct berval default_group_userPassword = BER_BVC("*"); /* unmatchable */ + +NSSOV_CBPRIV(group, + nssov_info *ni; + char buf[256]; + struct berval name; + struct berval gidnum; + struct berval user; + int wantmembers;); + +/* create a search filter for searching a group entry + by member uid, return -1 on errors */ +static int mkfilter_group_bymember(nssov_group_cbp *cbp,struct berval *buf) +{ + struct berval dn; + /* try to translate uid to DN */ + nssov_uid2dn(cbp->op,cbp->ni,&cbp->user,&dn); + if (BER_BVISNULL(&dn)) { + if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len + 6 > + buf->bv_len ) + return -1; + buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))", + cbp->mi->mi_filter.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val, + cbp->user.bv_val ); + } else { /* also lookup using user DN */ + if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len + + dn.bv_len + cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_len + 12 > buf->bv_len ) + return -1; + buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(|(%s=%s)(%s=%s)))", + cbp->mi->mi_filter.bv_val, + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val, cbp->user.bv_val, + cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_val, dn.bv_val ); + } + return 0; +} + +NSSOV_INIT(group) + +/* + Checks to see if the specified name is a valid group name. + + This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, + 3.189 Group Name and 3.276 Portable Filename Character Set): + http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_189 + http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276 + + The standard defines group names valid if they only contain characters from + the set [A-Za-z0-9._-] where the hyphen should not be used as first + character. +*/ +static int isvalidgroupname(struct berval *name) +{ + int i; + + if ( !name->bv_val || !name->bv_len ) + return 0; + /* check first character */ + if ( ! ( (name->bv_val[0]>='A' && name->bv_val[0] <= 'Z') || + (name->bv_val[0]>='a' && name->bv_val[0] <= 'z') || + (name->bv_val[0]>='0' && name->bv_val[0] <= '9') || + name->bv_val[0]=='.' || name->bv_val[0]=='_' ) ) + return 0; + /* 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') || + name->bv_val[i]=='.' || name->bv_val[i]=='_' || name->bv_val[i]=='-') ) + return 0; + } + /* no test failed so it must be good */ + return -1; +} + +static int write_group(nssov_group_cbp *cbp,Entry *entry) +{ + struct berval tmparr[2], tmpgid[2]; + struct berval *names,*gids,*members; + struct berval passwd = {0}; + Attribute *a; + int i,j,nummembers,rc = 0; + + /* get group name (cn) */ + if (BER_BVISNULL(&cbp->name)) + { + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val,0); + return 0; + } + names = a->a_vals; + } + else + { + names=tmparr; + names[0]=cbp->name; + BER_BVZERO(&names[1]); + } + /* get the group id(s) */ + if (BER_BVISNULL(&cbp->gidnum)) + { + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val,0); + return 0; + } + gids = a->a_vals; + } + else + { + gids=tmpgid; + gids[0]=cbp->gidnum; + BER_BVZERO(&gids[1]); + } + /* get group passwd (userPassword) (use only first entry) */ + a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc); + if (a) + get_userpassword(&a->a_vals[0], &passwd); + if (BER_BVISNULL(&passwd)) + passwd=default_group_userPassword; + /* get group members (memberUid&uniqueMember) */ + if (cbp->wantmembers) { + Attribute *b; + i = 0; j = 0; + a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc); + b = attr_find(entry->e_attrs, cbp->mi->mi_attrs[MEM_KEY].an_desc); + if ( a ) + i += a->a_numvals; + if ( b ) + i += b->a_numvals; + if ( i ) { + members = cbp->op->o_tmpalloc( (i+1) * sizeof(struct berval), cbp->op->o_tmpmemctx ); + + if ( a ) { + for (i=0; i<a->a_numvals; i++) { + if (isvalidusername(&a->a_vals[i])) { + ber_dupbv_x(&members[j],&a->a_vals[i],cbp->op->o_tmpmemctx); + j++; + } + } + } + a = b; + if ( a ) { + for (i=0; i<a->a_numvals; i++) { + if (nssov_dn2uid(cbp->op,cbp->ni,&a->a_nvals[i],&members[j])) + j++; + } + } + nummembers = j; + BER_BVZERO(&members[j]); + } else { + members=NULL; + nummembers = 0; + } + + } else { + members=NULL; + nummembers = 0; + } + /* write entries for all names and gids */ + for (i=0;!BER_BVISNULL(&names[i]);i++) + { + if (!isvalidgroupname(&names[i])) + { + 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 + { + for (j=0;!BER_BVISNULL(&gids[j]);j++) + { + char *tmp; + int tmpint32; + 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\"\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val, + names[i].bv_val); + continue; + } + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + WRITE_BERVAL(cbp->fp,&names[i]); + WRITE_BERVAL(cbp->fp,&passwd); + WRITE_INT32(cbp->fp,gid); + /* write a list of values */ + WRITE_INT32(cbp->fp,nummembers); + if (nummembers) + { + int k; + for (k=0;k<nummembers;k++) { + WRITE_BERVAL(cbp->fp,&members[k]); + } + } + } + } + } + /* free and return */ + if (members!=NULL) + ber_bvarray_free_x( members, cbp->op->o_tmpmemctx ); + return rc; +} + +NSSOV_CB(group) + +NSSOV_HANDLE( + group,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_STRING(fp,cbp.buf); + 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\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)\n",cbp.name.bv_val,0,0);, + NSLCD_ACTION_GROUP_BYNAME, + nssov_filter_byname(cbp.mi,CN_KEY,&cbp.name,&filter) +) + +NSSOV_HANDLE( + group,bygid, + gid_t gid; + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_INT32(fp,gid); + cbp.gidnum.bv_val = cbp.buf; + cbp.gidnum.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",gid); + cbp.wantmembers = 1; + cbp.ni = ni; + BER_BVZERO(&cbp.name); + BER_BVZERO(&cbp.user);, + 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) +) + +NSSOV_HANDLE( + group,bymember, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_STRING(fp,cbp.buf); + 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\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)\n",cbp.user.bv_val,0,0);, + NSLCD_ACTION_GROUP_BYMEMBER, + mkfilter_group_bymember(&cbp,&filter) +) + +NSSOV_HANDLE( + group,all, + struct berval filter; + /* no parameters to read */ + cbp.wantmembers = 1; + cbp.ni = ni; + BER_BVZERO(&cbp.name); + BER_BVZERO(&cbp.gidnum);, + Debug(LDAP_DEBUG_TRACE,"nssov_group_all()\n",0,0,0);, + NSLCD_ACTION_GROUP_ALL, + (filter=cbp.mi->mi_filter,0) +) diff --git a/contrib/slapd-modules/nssov/host.c b/contrib/slapd-modules/nssov/host.c new file mode 100644 index 0000000..c691b51 --- /dev/null +++ b/contrib/slapd-modules/nssov/host.c @@ -0,0 +1,161 @@ +/* host.c - host lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +/* ( nisSchema.2.6 NAME 'ipHost' SUP top AUXILIARY + * DESC 'Abstraction of a host, an IP device. The distinguished + * value of the cn attribute denotes the host's canonical + * name. Device SHOULD be used as a structural class' + * MUST ( cn $ ipHostNumber ) + * MAY ( l $ description $ manager ) ) + */ + +/* the basic search filter for searches */ +static struct berval host_filter = BER_BVC("(objectClass=ipHost)"); + +/* the attributes to request with searches */ +static struct berval host_keys[] = { + BER_BVC("cn"), + BER_BVC("ipHostNumber"), + BER_BVNULL +}; + +NSSOV_INIT(host) + +NSSOV_CBPRIV(host, + char buf[1024]; + struct berval name; + struct berval addr;); + +/* write a single host entry to the stream */ +static int write_host(nssov_host_cbp *cbp,Entry *entry) +{ + int32_t tmpint32; + int numaddr,i,numname,dupname; + struct berval name,*names,*addrs; + Attribute *a; + + /* get the most canonical name */ + nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name ); + /* get the other names for the host */ + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val, 0 ); + return 0; + } + names = a->a_vals; + numname = a->a_numvals; + /* if the name is not yet found, get the first entry from names */ + if (BER_BVISNULL(&name)) { + name=names[0]; + dupname = 0; + } else { + dupname = -1; + for (i=0; i<numname; i++) { + if ( bvmatch(&name, &a->a_nvals[i])) { + dupname = i; + break; + } + } + } + /* get the addresses */ + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 ); + return 0; + } + addrs = a->a_vals; + numaddr = a->a_numvals; + /* write the entry */ + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + WRITE_BERVAL(cbp->fp,&name); + if ( dupname >= 0 ) { + WRITE_INT32(cbp->fp,numname-1); + } else { + WRITE_INT32(cbp->fp,numname); + } + for (i=0;i<numname;i++) { + if (i == dupname) continue; + WRITE_BERVAL(cbp->fp,&names[i]); + } + WRITE_INT32(cbp->fp,numaddr); + for (i=0;i<numaddr;i++) + { + WRITE_ADDRESS(cbp->fp,&addrs[i]); + } + return 0; +} + +NSSOV_CB(host) + +NSSOV_HANDLE( + host,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + BER_BVZERO(&cbp.addr); + READ_STRING(fp,cbp.buf); + cbp.name.bv_len = tmpint32; + cbp.name.bv_val = cbp.buf;, + 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) +) + +NSSOV_HANDLE( + host,byaddr, + int af; + char addr[64]; + int len=sizeof(addr); + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + BER_BVZERO(&cbp.name); + READ_ADDRESS(fp,addr,len,af); + /* 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\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)\n",cbp.addr.bv_val,0,0);, + NSLCD_ACTION_HOST_BYADDR, + nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter) +) + +NSSOV_HANDLE( + host,all, + struct berval filter; + /* no parameters to read */ + BER_BVZERO(&cbp.name); + BER_BVZERO(&cbp.addr);, + 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 index 0000000..f6f7c9f --- /dev/null +++ b/contrib/slapd-modules/nssov/ldapns.schema @@ -0,0 +1,38 @@ +# $OpenLDAP$ +# $Id: ldapns.schema,v 1.3 2009-10-01 19:17:20 tedcheng 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} ) + +attributetype ( 1.3.6.1.4.1.5322.17.2.2 NAME 'loginStatus' + DESC 'Currently logged in sessions for a user' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + ORDERING caseIgnoreOrderingMatch + SYNTAX OMsDirectoryString ) + +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 ) + +objectclass ( 1.3.6.1.4.1.5322.17.1.3 NAME 'loginStatusObject' + DESC 'Auxiliary object class for login status attribute' + SUP top + AUXILIARY + MAY loginStatus ) diff --git a/contrib/slapd-modules/nssov/netgroup.c b/contrib/slapd-modules/nssov/netgroup.c new file mode 100644 index 0000000..98a7870 --- /dev/null +++ b/contrib/slapd-modules/nssov/netgroup.c @@ -0,0 +1,199 @@ +/* netgroup.c - netgroup lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" +#include <ac/ctype.h> + +/* ( nisSchema.2.8 NAME 'nisNetgroup' SUP top STRUCTURAL + * DESC 'Abstraction of a netgroup. May refer to other netgroups' + * MUST cn + * MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) ) + */ + +/* the basic search filter for searches */ +static struct berval netgroup_filter = BER_BVC("(objectClass=nisNetgroup)"); + +/* the attributes to request with searches */ +static struct berval netgroup_keys[] = { + BER_BVC("cn"), + BER_BVC("nisNetgroupTriple"), + BER_BVC("memberNisNetgroup"), + BER_BVNULL +}; + +NSSOV_INIT(netgroup) + +NSSOV_CBPRIV(netgroup, + char buf[256]; + struct berval name;); + +static int write_string_stripspace_len(TFILE *fp,const char *str,int len) +{ + int32_t tmpint32; + int i,j; + DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"",str); + if (str==NULL) + { + WRITE_INT32(fp,0); + } + else + { + /* skip leading spaces */ + for (i=0;(str[i]!='\0')&&(isspace(str[i]));i++) + /* nothing else to do */ ; + /* skip trailing spaces */ + for (j=len;(j>i)&&(isspace(str[j-1]));j--) + /* nothing else to do */ ; + /* write length of string */ + WRITE_INT32(fp,j-i); + /* write string itself */ + if (j>i) + { + WRITE(fp,str+i,j-i); + } + } + /* we're done */ + return 0; +} + +#define WRITE_STRING_STRIPSPACE_LEN(fp,str,len) \ + if (write_string_stripspace_len(fp,str,len)) \ + return -1; + +#define WRITE_STRING_STRIPSPACE(fp,str) \ + WRITE_STRING_STRIPSPACE_LEN(fp,str,strlen(str)) + +static int write_netgroup_triple(TFILE *fp,const char *triple) +{ + int32_t tmpint32; + int i; + int hostb,hoste,userb,usere,domainb,domaine; + /* skip leading spaces */ + for (i=0;(triple[i]!='\0')&&(isspace(triple[i]));i++) + /* nothing else to do */ ; + /* we should have a bracket now */ + if (triple[i]!='(') + { + Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): entry does not begin with '(' (entry skipped)\n",0,0,0); + return 0; + } + i++; + hostb=i; + /* find comma (end of host string) */ + for (;(triple[i]!='\0')&&(triple[i]!=',');i++) + /* nothing else to do */ ; + if (triple[i]!=',') + { + Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ',' (entry skipped)\n",0,0,0); + return 0; + } + hoste=i; + i++; + userb=i; + /* find comma (end of user string) */ + for (;(triple[i]!='\0')&&(triple[i]!=',');i++) + /* nothing else to do */ ; + if (triple[i]!=',') + { + Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ',' (entry skipped)\n",0,0,0); + return 0; + } + usere=i; + i++; + domainb=i; + /* find closing bracket (end of domain string) */ + for (;(triple[i]!='\0')&&(triple[i]!=')');i++) + /* nothing else to do */ ; + if (triple[i]!=')') + { + Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ')' (entry skipped)\n",0,0,0); + return 0; + } + domaine=i; + i++; + /* skip trailing spaces */ + for (;(triple[i]!='\0')&&(isspace(triple[i]));i++) + /* nothing else to do */ ; + /* 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)\n",0,0,0); + return 0; + } + /* write strings */ + WRITE_INT32(fp,NSLCD_RESULT_BEGIN); + WRITE_INT32(fp,NSLCD_NETGROUP_TYPE_TRIPLE); + WRITE_STRING_STRIPSPACE_LEN(fp,triple+hostb,hoste-hostb) + WRITE_STRING_STRIPSPACE_LEN(fp,triple+userb,usere-userb) + WRITE_STRING_STRIPSPACE_LEN(fp,triple+domainb,domaine-domainb) + /* we're done */ + return 0; +} + +static int write_netgroup(nssov_netgroup_cbp *cbp,Entry *entry) +{ + int32_t tmpint32; + int i; + Attribute *a; + + /* get the netgroup triples and member */ + a = attr_find(entry->e_attrs,cbp->mi->mi_attrs[1].an_desc); + if ( a ) { + /* write the netgroup triples */ + for (i=0;i<a->a_numvals;i++) + { + if (write_netgroup_triple(cbp->fp, a->a_vals[i].bv_val)) + return -1; + } + } + a = attr_find(entry->e_attrs,cbp->mi->mi_attrs[2].an_desc); + if ( a ) { + /* write netgroup members */ + for (i=0;i<a->a_numvals;i++) + { + /* write the result code */ + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + /* write triple indicator */ + WRITE_INT32(cbp->fp,NSLCD_NETGROUP_TYPE_NETGROUP); + /* write netgroup name */ + if (write_string_stripspace_len(cbp->fp,a->a_vals[i].bv_val,a->a_vals[i].bv_len)) + return -1; + } + } + /* we're done */ + return 0; +} + +NSSOV_CB(netgroup) + +NSSOV_HANDLE( + netgroup,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_STRING(fp,cbp.buf);, + cbp.name.bv_len = tmpint32; + cbp.name.bv_val = cbp.buf; + 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) +) diff --git a/contrib/slapd-modules/nssov/network.c b/contrib/slapd-modules/nssov/network.c new file mode 100644 index 0000000..0c027ba --- /dev/null +++ b/contrib/slapd-modules/nssov/network.c @@ -0,0 +1,161 @@ +/* network.c - network address lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +#include <ac/socket.h> + +/* ( nisSchema.2.7 NAME 'ipNetwork' SUP top STRUCTURAL + * DESC 'Abstraction of a network. The distinguished value of + * MUST ( cn $ ipNetworkNumber ) + * MAY ( ipNetmaskNumber $ l $ description $ manager ) ) + */ + +/* the basic search filter for searches */ +static struct berval network_filter = BER_BVC("(objectClass=ipNetwork)"); + +/* the attributes used in searches */ +static struct berval network_keys[] = { + BER_BVC("cn"), + BER_BVC("ipNetworkNumber"), + BER_BVNULL +}; + +NSSOV_INIT(network) + +NSSOV_CBPRIV(network, + char buf[1024]; + struct berval name; + struct berval addr;); + +/* write a single network entry to the stream */ +static int write_network(nssov_network_cbp *cbp,Entry *entry) +{ + int32_t tmpint32; + int numaddr,i,numname,dupname; + struct berval name, *names, *addrs; + Attribute *a; + + /* get the most canonical name */ + nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name); + /* get the other names for the network */ + 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\n", + entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val,0); + return 0; + } + names = a->a_vals; + numname = a->a_numvals; + /* if the name is not yet found, get the first entry from names */ + if (BER_BVISNULL(&name)) { + name=names[0]; + dupname = 0; + } else { + dupname = -1; + for (i=0; i<numname; i++) { + if ( bvmatch(&name, &a->a_nvals[i])) { + dupname = i; + break; + } + } + } + /* get the addresses */ + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 ); + return 0; + } + addrs = a->a_vals; + numaddr = a->a_numvals; + /* write the entry */ + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + WRITE_BERVAL(cbp->fp,&name); + if ( dupname >= 0 ) { + WRITE_INT32(cbp->fp,numname-1); + } else { + WRITE_INT32(cbp->fp,numname); + } + for (i=0;i<numname;i++) { + if (i == dupname) continue; + WRITE_BERVAL(cbp->fp,&names[i]); + } + WRITE_INT32(cbp->fp,numaddr); + for (i=0;i<numaddr;i++) + { + WRITE_ADDRESS(cbp->fp,&addrs[i]); + } + return 0; +} + +NSSOV_CB(network) + +NSSOV_HANDLE( + network,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + BER_BVZERO(&cbp.addr); + READ_STRING(fp,cbp.buf); + cbp.name.bv_len = tmpint32; + cbp.name.bv_val = cbp.buf;, + 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) +) + +NSSOV_HANDLE( + network,byaddr, + int af; + char addr[64]; + int len=sizeof(addr); + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + BER_BVZERO(&cbp.name); + READ_ADDRESS(fp,addr,len,af); + /* 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\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)\n",cbp.addr.bv_val,0,0);, + NSLCD_ACTION_NETWORK_BYADDR, + nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter) +) + +NSSOV_HANDLE( + network,all, + struct berval filter; + /* no parameters to read */ + BER_BVZERO(&cbp.name); + BER_BVZERO(&cbp.addr);, + Debug(LDAP_DEBUG_TRACE,"nssov_network_all()\n",0,0,0);, + NSLCD_ACTION_NETWORK_ALL, + (filter=cbp.mi->mi_filter,0) +) diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/README b/contrib/slapd-modules/nssov/nss-pam-ldapd/README new file mode 100644 index 0000000..4176ad7 --- /dev/null +++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/README @@ -0,0 +1,15 @@ +These files were pulled from the nss-pam-ldapd project version 0.9.4. +Copyright notices are in the individual files. + +This is not the full distribution of nss-pam-ldapd, and does not +include the client-side stub libraries. Get the latest release of +nss-pam-ldapd from http://arthurdejong.org/nss-pam-ldapd/ to use +this overlay. + +If your system already has the nss-pam-ldapd stub libraries +installed, make sure the versions match the version number +shown above. Otherwise, there may be incompatible differences in +the protocols being used. Currently nssov requires at least +version 0.9.0. If your system's version is older, you will need +to install the client-side stubs from source. + diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h new file mode 100644 index 0000000..83f1777 --- /dev/null +++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h @@ -0,0 +1,91 @@ +/* + attrs.h - wrapper macros for the gcc __attribute__(()) directive + + Copyright (C) 2007, 2008, 2012 Arthur de Jong + + 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 +*/ + +#ifndef COMPAT__ATTRS_H +#define COMPAT__ATTRS_H 1 + +/* macro for testing the version of GCC */ +#define GCC_VERSION(major, minor) \ + ((__GNUC__ > (major)) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) + +/* These are macros to use some gcc-specific flags in case the're available + and otherwise define them to empty strings. This allows us to give + the compiler some extra information. + See http://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html + for a list of attributes supported by gcc */ + +/* this is used to flag function parameters that are not used in the function + body. */ +#if GCC_VERSION(3, 0) +#define UNUSED(x) x __attribute__((__unused__)) +#else +#define UNUSED(x) x +#endif + +/* this is used to add extra format checking to the function calls as if this + was a printf()-like function */ +#if GCC_VERSION(3, 0) +#define LIKE_PRINTF(format_idx, arg_idx) \ + __attribute__((__format__(__printf__, format_idx, arg_idx))) +#else +#define LIKE_PRINTF(format_idx, arg_idx) /* no attribute */ +#endif + +/* indicates that the function is "pure": its result is purely based on + the parameters and has no side effects or used static data */ +#if GCC_VERSION(3, 0) +#define PURE __attribute__((__pure__)) +#else +#define PURE /* no attribute */ +#endif + +/* the function returns a new data structure that has been freshly + allocated */ +#if GCC_VERSION(3, 0) +#define LIKE_MALLOC __attribute__((__malloc__)) +#else +#define LIKE_MALLOC /* no attribute */ +#endif + +/* the function's return value should be used by the caller */ +#if GCC_VERSION(3, 4) +#define MUST_USE __attribute__((__warn_unused_result__)) +#else +#define MUST_USE /* no attribute */ +#endif + +/* the function's return value should be used by the caller */ +#if GCC_VERSION(2, 5) +#define NORETURN __attribute__((__noreturn__)) +#else +#define NORETURN /* no attribute */ +#endif + +/* define __STRING if it's not yet defined */ +#ifndef __STRING +#ifdef __STDC__ +#define __STRING(x) #x +#else /* __STDC__ */ +#define __STRING(x) "x" +#endif /* not __STDC__ */ +#endif /* not __STRING */ + +#endif /* not COMPAT__ATTRS_H */ diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h new file mode 100644 index 0000000..649be89 --- /dev/null +++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h @@ -0,0 +1,381 @@ +/* + nslcd-prot.h - helper macros for reading and writing in protocol streams + + Copyright (C) 2006 West Consulting + Copyright (C) 2006-2014 Arthur de Jong + + 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 +*/ + +#ifndef COMMON__NSLCD_PROT_H +#define COMMON__NSLCD_PROT_H 1 + +#include <arpa/inet.h> +#include <netinet/in.h> + +#include "tio.h" + +/* If you use these macros you should define the following macros to + handle error conditions (these marcos should clean up and return from the + function): + ERROR_OUT_WRITEERROR(fp) + ERROR_OUT_READERROR(fp) + ERROR_OUT_BUFERROR(fp) + ERROR_OUT_NOSUCCESS(fp) */ + + +/* Debugging marcos that can be used to enable detailed protocol logging, + pass -DDEBUG_PROT to do overall protocol debugging, and -DDEBUG_PROT_DUMP + to dump the actual bytestream. */ + +#ifdef DEBUG_PROT +/* define a debugging macro to output logging */ +#include <string.h> +#include <errno.h> +#define DEBUG_PRINT(fmt, arg) \ + fprintf(stderr, "%s:%d:%s: " fmt "\n", __FILE__, __LINE__, \ + __PRETTY_FUNCTION__, arg); +#else /* DEBUG_PROT */ +/* define an empty debug macro to disable logging */ +#define DEBUG_PRINT(fmt, arg) +#endif /* not DEBUG_PROT */ + +#ifdef DEBUG_PROT_DUMP +/* define a debugging macro to output detailed logging */ +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif /* HAVE_STDINT_H */ +static void debug_dump(const void *ptr, size_t size) +{ + int i; + for (i = 0; i < size; i++) + fprintf(stderr, " %02x", ((const uint8_t *)ptr)[i]); + fprintf(stderr, "\n"); +} +#define DEBUG_DUMP(ptr, size) \ + fprintf(stderr, "%s:%d:%s:", __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + debug_dump(ptr, size); +#else /* DEBUG_PROT_DUMP */ +/* define an empty debug macro to disable logging */ +#define DEBUG_DUMP(ptr, size) +#endif /* not DEBUG_PROT_DUMP */ + + +/* WRITE marcos, used for writing data, on write error they will + call the ERROR_OUT_WRITEERROR macro + these macros may require the availability of the following + variables: + int32_t tmpint32; - temporary variable + */ + +#define WRITE(fp, ptr, size) \ + DEBUG_PRINT("WRITE : var="__STRING(ptr)" size=%d", (int)size); \ + DEBUG_DUMP(ptr, size); \ + if (tio_write(fp, ptr, (size_t)size)) \ + { \ + DEBUG_PRINT("WRITE : var="__STRING(ptr)" error: %s", \ + strerror(errno)); \ + ERROR_OUT_WRITEERROR(fp); \ + } + +#define WRITE_INT32(fp, i) \ + DEBUG_PRINT("WRITE_INT32 : var="__STRING(i)" int32=%08x", (int)i); \ + tmpint32 = htonl((int32_t)(i)); \ + WRITE(fp, &tmpint32, sizeof(int32_t)) + +#define WRITE_STRING(fp, str) \ + DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"", (str)); \ + if ((str) == NULL) \ + { \ + WRITE_INT32(fp, 0); \ + } \ + else \ + { \ + WRITE_INT32(fp, strlen(str)); \ + tmpint32 = ntohl(tmpint32); \ + if (tmpint32 > 0) \ + { \ + WRITE(fp, (str), tmpint32); \ + } \ + } + +#define WRITE_STRINGLIST(fp, arr) \ + if ((arr) == NULL) \ + { \ + DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", 0); \ + WRITE_INT32(fp, 0); \ + } \ + else \ + { \ + /* first determine length of array */ \ + for (tmp3int32 = 0; (arr)[tmp3int32] != NULL; tmp3int32++) \ + /* noting */ ; \ + /* write number of strings */ \ + DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \ + WRITE_INT32(fp, tmp3int32); \ + /* write strings */ \ + for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \ + { \ + WRITE_STRING(fp, (arr)[tmp2int32]); \ + } \ + } + +#define WRITE_STRINGLIST_EXCEPT(fp, arr, not) \ + /* first determine length of array */ \ + tmp3int32 = 0; \ + for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \ + if (strcmp((arr)[tmp2int32], (not)) != 0) \ + tmp3int32++; \ + /* write number of strings (mius one because we intend to skip one) */ \ + DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \ + WRITE_INT32(fp, tmp3int32); \ + /* write strings */ \ + for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \ + { \ + if (strcmp((arr)[tmp2int32], (not)) != 0) \ + { \ + WRITE_STRING(fp, (arr)[tmp2int32]); \ + } \ + } + +/* READ macros, used for reading data, on read error they will + call the ERROR_OUT_READERROR or ERROR_OUT_BUFERROR macro + these macros may require the availability of the following + variables: + int32_t tmpint32; - temporary variable + */ + +#define READ(fp, ptr, size) \ + if (tio_read(fp, ptr, (size_t)size)) \ + { \ + DEBUG_PRINT("READ : var="__STRING(ptr)" error: %s", \ + strerror(errno)); \ + ERROR_OUT_READERROR(fp); \ + } \ + DEBUG_PRINT("READ : var="__STRING(ptr)" size=%d", (int)(size)); \ + DEBUG_DUMP(ptr, size); + +#define READ_INT32(fp, i) \ + READ(fp, &tmpint32, sizeof(int32_t)); \ + (i) = (int32_t)ntohl(tmpint32); \ + DEBUG_PRINT("READ_INT32 : var="__STRING(i)" int32==%08x", (int)(i)); + +/* read a string in a fixed-size "normal" buffer */ +#define READ_STRING(fp, buffer) \ + /* read the size of the string */ \ + READ(fp, &tmpint32, sizeof(int32_t)); \ + tmpint32 = ntohl(tmpint32); \ + DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" strlen=%d", tmpint32); \ + /* check if read would fit */ \ + if (((size_t)tmpint32) >= sizeof(buffer)) \ + { \ + /* will not fit */ \ + tmpint32 = (tmpint32 - sizeof(buffer)) + 1; \ + DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \ + ERROR_OUT_BUFERROR(fp); \ + } \ + /* read string from the stream */ \ + if (tmpint32 > 0) \ + { \ + READ(fp, buffer, (size_t)tmpint32); \ + } \ + /* null-terminate string in buffer */ \ + buffer[tmpint32] = '\0'; \ + DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" string=\"%s\"", buffer); + + +/* READ BUF macros that read data into a pre-allocated buffer. + these macros may require the availability of the following + variables: + int32_t tmpint32; - temporary variable + char *buffer; - pointer to a buffer for reading strings + size_t buflen; - the size of the buffer + size_t bufptr; - the current position in the buffer + */ + +/* current position in the buffer */ +#define BUF_CUR \ + (buffer + bufptr) + +/* check that the buffer has sz bytes left in it */ +#define BUF_CHECK(fp, sz) \ + if ((bufptr + (size_t)(sz)) > buflen) \ + { \ + /* will not fit */ \ + tmpint32 = bufptr + (sz) - (buflen); \ + DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \ + ERROR_OUT_BUFERROR(fp); \ + } + +/* move the buffer pointer */ +#define BUF_SKIP(sz) \ + bufptr += (size_t)(sz); + +/* move BUF_CUR foreward so that it is aligned to the specified + type width */ +#define BUF_ALIGN(fp, type) \ + /* figure out number of bytes to skip foreward */ \ + tmp2int32 = (sizeof(type) - ((BUF_CUR - (char *)NULL) % sizeof(type))) \ + % sizeof(type); \ + /* check and skip */ \ + BUF_CHECK(fp, tmp2int32); \ + BUF_SKIP(tmp2int32); + +/* allocate a piece of the buffer to store an array in */ +#define BUF_ALLOC(fp, ptr, type, num) \ + /* align to the specified type width */ \ + BUF_ALIGN(fp, type); \ + /* check that we have enough room */ \ + BUF_CHECK(fp, (size_t)(num) * sizeof(type)); \ + /* store the pointer */ \ + (ptr) = (type *)BUF_CUR; \ + /* reserve the space */ \ + BUF_SKIP((size_t)(num) * sizeof(type)); + +/* read a binary blob into the buffer */ +#define READ_BUF(fp, ptr, sz) \ + /* check that there is enough room and read */ \ + BUF_CHECK(fp, sz); \ + READ(fp, BUF_CUR, (size_t)sz); \ + /* store pointer and skip */ \ + (ptr) = BUF_CUR; \ + BUF_SKIP(sz); + +/* read string in the buffer (using buffer, buflen and bufptr) + and store the actual location of the string in field */ +#define READ_BUF_STRING(fp, field) \ + /* read the size of the string */ \ + READ(fp, &tmpint32, sizeof(int32_t)); \ + tmpint32 = ntohl(tmpint32); \ + DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" strlen=%d", tmpint32); \ + /* check if read would fit */ \ + BUF_CHECK(fp, tmpint32 + 1); \ + /* read string from the stream */ \ + if (tmpint32 > 0) \ + { \ + READ(fp, BUF_CUR, (size_t)tmpint32); \ + } \ + /* null-terminate string in buffer */ \ + BUF_CUR[tmpint32] = '\0'; \ + DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" string=\"%s\"", BUF_CUR); \ + /* prepare result */ \ + (field) = BUF_CUR; \ + BUF_SKIP(tmpint32 + 1); + +/* read an array from a stram and store it as a null-terminated + array list (size for the array is allocated) */ +#define READ_BUF_STRINGLIST(fp, arr) \ + /* read the number of entries */ \ + READ(fp, &tmp3int32, sizeof(int32_t)); \ + tmp3int32 = ntohl(tmp3int32); \ + DEBUG_PRINT("READ_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \ + /* allocate room for *char[num + 1] */ \ + BUF_ALLOC(fp, arr, char *, tmp3int32 + 1); \ + /* read all entries */ \ + for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \ + { \ + READ_BUF_STRING(fp, (arr)[tmp2int32]); \ + } \ + /* set last entry to NULL */ \ + (arr)[tmp2int32] = NULL; + + +/* SKIP macros for skipping over certain parts of the protocol stream. */ + +/* skip a number of bytes foreward */ +#define SKIP(fp, sz) \ + DEBUG_PRINT("READ : skip %d bytes", (int)(sz)); \ + /* read (skip) the specified number of bytes */ \ + if (tio_skip(fp, sz)) \ + { \ + DEBUG_PRINT("READ : skip error: %s", strerror(errno)); \ + ERROR_OUT_READERROR(fp); \ + } + +/* read a string from the stream but don't do anything with the result */ +#define SKIP_STRING(fp) \ + /* read the size of the string */ \ + READ(fp, &tmpint32, sizeof(int32_t)); \ + tmpint32 = ntohl(tmpint32); \ + DEBUG_PRINT("READ_STRING: skip %d bytes", (int)tmpint32); \ + /* read (skip) the specified number of bytes */ \ + SKIP(fp, tmpint32); + +/* skip a list of strings */ +#define SKIP_STRINGLIST(fp) \ + /* read the number of entries */ \ + READ(fp, &tmp3int32, sizeof(int32_t)); \ + tmp3int32 = ntohl(tmp3int32); \ + DEBUG_PRINT("READ_STRLST: skip %d strings", (int)tmp3int32); \ + /* read all entries */ \ + for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \ + { \ + SKIP_STRING(fp); \ + } + + +/* These are functions and macors for performing common operations in + the nslcd request/response protocol. */ + +/* returns a socket to the server or NULL on error (see errno), + socket should be closed with tio_close() */ +TFILE *nslcd_client_open(void) + MUST_USE; + +/* generic request code */ +#define NSLCD_REQUEST(fp, action, writefn) \ + /* open a client socket */ \ + if ((fp = nslcd_client_open()) == NULL) \ + { \ + ERROR_OUT_OPENERROR; \ + } \ + /* write a request header with a request code */ \ + WRITE_INT32(fp, (int32_t)NSLCD_VERSION) \ + WRITE_INT32(fp, (int32_t)action) \ + /* write the request parameters (if any) */ \ + writefn; \ + /* flush the stream */ \ + if (tio_flush(fp) < 0) \ + { \ + DEBUG_PRINT("WRITE_FLUSH : error: %s", strerror(errno)); \ + ERROR_OUT_WRITEERROR(fp); \ + } \ + /* read and check response version number */ \ + READ(fp, &tmpint32, sizeof(int32_t)); \ + tmpint32 = ntohl(tmpint32); \ + if (tmpint32 != (int32_t)NSLCD_VERSION) \ + { \ + ERROR_OUT_READERROR(fp); \ + } \ + /* read and check response request number */ \ + READ(fp, &tmpint32, sizeof(int32_t)); \ + tmpint32 = ntohl(tmpint32); \ + if (tmpint32 != (int32_t)(action)) \ + { \ + ERROR_OUT_READERROR(fp); \ + } + +/* Read the response code (the result code of the query) from + the stream. */ +#define READ_RESPONSE_CODE(fp) \ + READ(fp, &tmpint32, sizeof(int32_t)); \ + tmpint32 = ntohl(tmpint32); \ + if (tmpint32 != (int32_t)NSLCD_RESULT_BEGIN) \ + { \ + ERROR_OUT_NOSUCCESS(fp); \ + } + +#endif /* not COMMON__NSLCD_PROT_H */ diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h new file mode 100644 index 0000000..9884733 --- /dev/null +++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h @@ -0,0 +1,305 @@ +/* + nslcd.h - file describing client/server protocol + + Copyright (C) 2006 West Consulting + Copyright (C) 2006, 2007, 2009, 2010, 2011, 2012, 2013 Arthur de Jong + + 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 +*/ + +#ifndef _NSLCD_H +#define _NSLCD_H 1 + +/* + The protocol used between the nslcd client and server is a simple binary + protocol. It is request/response based where the client initiates a + connection, does a single request and closes the connection again. Any + mangled or not understood messages will be silently ignored by the server. + + A request looks like: + INT32 NSLCD_VERSION + INT32 NSLCD_ACTION_* + [request parameters if any] + A response looks like: + INT32 NSLCD_VERSION + INT32 NSLCD_ACTION_* (the original request type) + [result(s)] + INT32 NSLCD_RESULT_END + A single result entry looks like: + INT32 NSLCD_RESULT_BEGIN + [result value(s)] + If a response would return multiple values (e.g. for NSLCD_ACTION_*_ALL + functions) each return value will be preceded by a NSLCD_RESULT_BEGIN + value. After the last returned result the server sends + NSLCD_RESULT_END. If some error occurs (e.g. LDAP server unavailable, + error in the request, etc) the server terminates the connection to signal + an error condition (breaking the protocol). + + These are the available basic data types: + INT32 - 32-bit integer value + TYPE - a typed field that is transferred using sizeof() + STRING - a string length (32bit) followed by the string value (not + null-terminted) the string itself is assumed to be UTF-8 + STRINGLIST - a 32-bit number noting the number of strings followed by + the strings one at a time + + Furthermore the ADDRESS compound data type is defined as: + INT32 type of address: e.g. AF_INET or AF_INET6 + INT32 lenght of address + RAW the address itself + With the ADDRESSLIST using the same construct as with STRINGLIST. + + The protocol uses network byte order for all types. +*/ + +/* The current version of the protocol. This protocol should only be + updated with major backwards-incompatible changes. */ +#define NSLCD_VERSION 0x00000002 + +/* Get a NSLCD configuration option. There is one request parameter: + INT32 NSLCD_CONFIG_* + the result value is: + STRING value, interpretation depending on request */ +#define NSLCD_ACTION_CONFIG_GET 0x00010001 + +/* return the message, if any, that is presented to the user when password + modification through PAM is prohibited */ +#define NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE 1 + +/* Email alias (/etc/aliases) NSS requests. The result values for a + single entry are: + STRING alias name + STRINGLIST alias rcpts */ +#define NSLCD_ACTION_ALIAS_BYNAME 0x00020001 +#define NSLCD_ACTION_ALIAS_ALL 0x00020008 + +/* Ethernet address/name mapping NSS requests. The result values for a + single entry are: + STRING ether name + TYPE(uint8_t[6]) ether address */ +#define NSLCD_ACTION_ETHER_BYNAME 0x00030001 +#define NSLCD_ACTION_ETHER_BYETHER 0x00030002 +#define NSLCD_ACTION_ETHER_ALL 0x00030008 + +/* Group and group membership related NSS requests. The result values + for a single entry are: + STRING group name + STRING group password + INT32 group id + STRINGLIST members (usernames) of the group + (not that the BYMEMER call returns an empty members list) */ +#define NSLCD_ACTION_GROUP_BYNAME 0x00040001 +#define NSLCD_ACTION_GROUP_BYGID 0x00040002 +#define NSLCD_ACTION_GROUP_BYMEMBER 0x00040006 +#define NSLCD_ACTION_GROUP_ALL 0x00040008 + +/* Hostname (/etc/hosts) lookup NSS requests. The result values + for an entry are: + STRING host name + STRINGLIST host aliases + ADDRESSLIST host addresses */ +#define NSLCD_ACTION_HOST_BYNAME 0x00050001 +#define NSLCD_ACTION_HOST_BYADDR 0x00050002 +#define NSLCD_ACTION_HOST_ALL 0x00050008 + +/* Netgroup NSS result entries contain a number of parts. A result entry + starts with: + STRING netgroup name + followed by zero or more references to other netgroups or netgroup + triples. A reference to another netgroup looks like: + INT32 NSLCD_NETGROUP_TYPE_NETGROUP + STRING other netgroup name + A a netgroup triple looks like: + INT32 NSLCD_NETGROUP_TYPE_TRIPLE + STRING host + STRING user + STRING domain + A netgroup result entry is terminated by: + INT32 NSLCD_NETGROUP_TYPE_END + */ +#define NSLCD_ACTION_NETGROUP_BYNAME 0x00060001 +#define NSLCD_ACTION_NETGROUP_ALL 0x00060008 +#define NSLCD_NETGROUP_TYPE_NETGROUP 1 +#define NSLCD_NETGROUP_TYPE_TRIPLE 2 +#define NSLCD_NETGROUP_TYPE_END 3 + +/* Network name (/etc/networks) NSS requests. Result values for a single + entry are: + STRING network name + STRINGLIST network aliases + ADDRESSLIST network addresses */ +#define NSLCD_ACTION_NETWORK_BYNAME 0x00070001 +#define NSLCD_ACTION_NETWORK_BYADDR 0x00070002 +#define NSLCD_ACTION_NETWORK_ALL 0x00070008 + +/* User account (/etc/passwd) NSS requests. Result values are: + STRING user name + STRING user password + INT32 user id + INT32 group id + STRING gecos information + STRING home directory + STRING login shell */ +#define NSLCD_ACTION_PASSWD_BYNAME 0x00080001 +#define NSLCD_ACTION_PASSWD_BYUID 0x00080002 +#define NSLCD_ACTION_PASSWD_ALL 0x00080008 + +/* Protocol information requests. Result values are: + STRING protocol name + STRINGLIST protocol aliases + INT32 protocol number */ +#define NSLCD_ACTION_PROTOCOL_BYNAME 0x00090001 +#define NSLCD_ACTION_PROTOCOL_BYNUMBER 0x00090002 +#define NSLCD_ACTION_PROTOCOL_ALL 0x00090008 + +/* RPC information requests. Result values are: + STRING rpc name + STRINGLIST rpc aliases + INT32 rpc number */ +#define NSLCD_ACTION_RPC_BYNAME 0x000a0001 +#define NSLCD_ACTION_RPC_BYNUMBER 0x000a0002 +#define NSLCD_ACTION_RPC_ALL 0x000a0008 + +/* Service (/etc/services) information requests. The BYNAME and BYNUMBER + requests contain an extra protocol string in the request which, if not + blank, will filter the services by this protocol. Result values are: + STRING service name + STRINGLIST service aliases + INT32 service (port) number + STRING service protocol */ +#define NSLCD_ACTION_SERVICE_BYNAME 0x000b0001 +#define NSLCD_ACTION_SERVICE_BYNUMBER 0x000b0002 +#define NSLCD_ACTION_SERVICE_ALL 0x000b0008 + +/* Extended user account (/etc/shadow) information requests. Result + values for a single entry are: + STRING user name + STRING user password + INT32 last password change + INT32 mindays + INT32 maxdays + INT32 warn + INT32 inact + INT32 expire + INT32 flag */ +#define NSLCD_ACTION_SHADOW_BYNAME 0x000c0001 +#define NSLCD_ACTION_SHADOW_ALL 0x000c0008 + +/* PAM-related requests. The request parameters for all these requests + begin with: + STRING user name + STRING service name + STRING ruser + STRING rhost + STRING tty + If the user is not known in LDAP no result may be returned (immediately + return NSLCD_RESULT_END instead of a PAM error code). */ + +/* PAM authentication check request. The extra request values are: + STRING password + and the result value consists of: + INT32 authc NSLCD_PAM_* result code + STRING user name (the cannonical user name) + INT32 authz NSLCD_PAM_* result code + STRING authorisation error message + If the username is empty in this request an attempt is made to + authenticate as the administrator (set using rootpwmoddn). + Some authorisation checks are already done during authentication so the + response also includes authorisation information. */ +#define NSLCD_ACTION_PAM_AUTHC 0x000d0001 + +/* PAM authorisation check request. The result value consists of: + INT32 authz NSLCD_PAM_* result code + STRING authorisation error message + The authentication check may have already returned some authorisation + information. The authorisation error message, if supplied, will be used + by the PAM module instead of a message that is generated by the PAM + module itself. */ +#define NSLCD_ACTION_PAM_AUTHZ 0x000d0002 + +/* PAM session open request. The result value consists of: + STRING session id + This session id may be used to close this session with. */ +#define NSLCD_ACTION_PAM_SESS_O 0x000d0003 + +/* PAM session close request. This request has the following + extra request value: + STRING session id + and this calls only returns an empty response value. */ +#define NSLCD_ACTION_PAM_SESS_C 0x000d0004 + +/* PAM password modification request. This requests has the following extra + request values: + INT32 asroot: 0=oldpasswd is user passwd, 1=oldpasswd is root passwd + STRING old password + STRING new password + and returns there extra result values: + INT32 NSLCD_PAM_* result code + STRING error message */ +#define NSLCD_ACTION_PAM_PWMOD 0x000d0005 + +/* User information change request. This request allows one to change + their full name and other information. The request parameters for this + request are: + STRING user name + INT32 asroot: 0=passwd is user passwd, 1=passwd is root passwd + STRING password + followed by one or more of the below, terminated by NSLCD_USERMOD_END + INT32 NSLCD_USERMOD_* + STRING new value + the response consists of one or more of the entries below, terminated + by NSLCD_USERMOD_END: + INT32 NSLCD_USERMOD_* + STRING response + (if the response is blank, the change went OK, otherwise the string + contains an error message) + */ +#define NSLCD_ACTION_USERMOD 0x000e0001 + +/* These are the possible values for the NSLCD_ACTION_USERMOD operation + above. */ +#define NSLCD_USERMOD_END 0 /* end of change values */ +#define NSLCD_USERMOD_RESULT 1 /* global result value */ +#define NSLCD_USERMOD_FULLNAME 2 /* full name */ +#define NSLCD_USERMOD_ROOMNUMBER 3 /* room number */ +#define NSLCD_USERMOD_WORKPHONE 4 /* office phone number */ +#define NSLCD_USERMOD_HOMEPHONE 5 /* home phone number */ +#define NSLCD_USERMOD_OTHER 6 /* other info */ +#define NSLCD_USERMOD_HOMEDIR 7 /* home directory */ +#define NSLCD_USERMOD_SHELL 8 /* login shell */ + +/* Request result codes. */ +#define NSLCD_RESULT_BEGIN 1 +#define NSLCD_RESULT_END 2 + +/* 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_ERR 20 /* Authentication token manipulation error */ +#define NSLCD_PAM_AUTHTOK_DISABLE_AGING 23 /* Password aging disabled */ +#define NSLCD_PAM_IGNORE 25 /* Ignore module */ +#define NSLCD_PAM_ABORT 26 /* Fatal error */ +#define NSLCD_PAM_AUTHTOK_EXPIRED 27 /* authentication token has expired */ + +#endif /* not _NSLCD_H */ diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c new file mode 100644 index 0000000..11ad4f7 --- /dev/null +++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c @@ -0,0 +1,520 @@ +/* + tio.c - timed io functions + This file is part of the nss-pam-ldapd library. + + Copyright (C) 2007-2014 Arthur de Jong + + 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 "portable.h" + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif /* HAVE_STDINT_H */ +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <stdio.h> +#include <limits.h> +#include <poll.h> +#include <time.h> + +#include "tio.h" + +/* for platforms that don't have ETIME use ETIMEDOUT */ +#ifndef ETIME +#define ETIME ETIMEDOUT +#endif /* ETIME */ + +/* structure that holds a buffer + the buffer contains the data that is between the application and the + file descriptor that is used for efficient transfer + the buffer is built up as follows: + |.....********......| + ^start ^size + ^--len--^ */ +struct tio_buffer { + uint8_t *buffer; + size_t size; /* the size of the buffer */ + size_t maxsize; /* the maximum size of the buffer */ + size_t start; /* the start of the data (before start is unused) */ + size_t len; /* size of the data (from the start) */ +}; + +/* structure that holds all the state for files */ +struct tio_fileinfo { + int fd; + struct tio_buffer readbuffer; + struct tio_buffer writebuffer; + int readtimeout; + int writetimeout; + int read_resettable; /* whether the tio_reset() function can be called */ +#ifdef DEBUG_TIO_STATS + /* this is used to collect statistics on the use of the streams + and can be used to tune the buffer sizes */ + size_t byteswritten; + size_t bytesread; +#endif /* DEBUG_TIO_STATS */ +}; + +/* some older versions of Solaris don't provide CLOCK_MONOTONIC but do have + a CLOCK_HIGHRES that has the same properties we need */ +#ifndef CLOCK_MONOTONIC +#ifdef CLOCK_HIGHRES +#define CLOCK_MONOTONIC CLOCK_HIGHRES +#endif /* CLOCK_HIGHRES */ +#endif /* not CLOCK_MONOTONIC */ + +/* update the timeout to the value that is remaining before the deadline + returns the number of milliseconds before the deadline (or a negative + value of the deadline has expired) */ +static inline int tio_time_remaining(struct timespec *deadline, int timeout) +{ + struct timespec tv; + /* if this is the first call, set the deadline and return the full time */ + if ((deadline->tv_sec == 0) && (deadline->tv_nsec == 0)) + { + if (clock_gettime(CLOCK_MONOTONIC, deadline) == 0) + { + deadline->tv_sec += timeout / 1000; + deadline->tv_nsec += (timeout % 1000) * 1000000; + } + return timeout; + } + /* get the current time (fall back to full time on error) */ + if (clock_gettime(CLOCK_MONOTONIC, &tv)) + return timeout; + /* calculate time remaining in milliseconds */ + return (deadline->tv_sec - tv.tv_sec) * 1000 + + (deadline->tv_nsec - tv.tv_nsec) / 1000000; +} + +/* open a new TFILE based on the file descriptor */ +TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout, + size_t initreadsize, size_t maxreadsize, + size_t initwritesize, size_t maxwritesize) +{ + struct tio_fileinfo *fp; + fp = (struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo)); + if (fp == NULL) + return NULL; + fp->fd = fd; + /* initialize read buffer */ + fp->readbuffer.buffer = (uint8_t *)malloc(initreadsize); + if (fp->readbuffer.buffer == NULL) + { + free(fp); + return NULL; + } + fp->readbuffer.size = initreadsize; + fp->readbuffer.maxsize = maxreadsize; + fp->readbuffer.start = 0; + fp->readbuffer.len = 0; + /* initialize write buffer */ + fp->writebuffer.buffer = (uint8_t *)malloc(initwritesize); + if (fp->writebuffer.buffer == NULL) + { + free(fp->readbuffer.buffer); + free(fp); + return NULL; + } + fp->writebuffer.size = initwritesize; + fp->writebuffer.maxsize = maxwritesize; + fp->writebuffer.start = 0; + fp->writebuffer.len = 0; + /* initialize other attributes */ + fp->readtimeout = readtimeout; + fp->writetimeout = writetimeout; + fp->read_resettable = 0; +#ifdef DEBUG_TIO_STATS + fp->byteswritten = 0; + fp->bytesread = 0; +#endif /* DEBUG_TIO_STATS */ + return fp; +} + +/* wait for any activity on the specified file descriptor using + the specified deadline */ +static int tio_wait(int fd, short events, int timeout, + struct timespec *deadline) +{ + int t; + struct pollfd fds[1]; + int rv; + while (1) + { + fds[0].fd = fd; + fds[0].events = events; + /* figure out the time we need to wait */ + if ((t = tio_time_remaining(deadline, timeout)) < 0) + { + errno = ETIME; + return -1; + } + /* sanitiy check for moving clock */ + if (t > timeout) + t = timeout; + /* wait for activity */ + rv = poll(fds, 1, t); + if (rv > 0) + return 0; /* we have activity */ + else if (rv == 0) + { + /* no file descriptors were available within the specified time */ + errno = ETIME; + return -1; + } + else if ((errno != EINTR) && (errno != EAGAIN)) + /* some error ocurred */ + return -1; + /* we just try again on EINTR or EAGAIN */ + } +} + +/* do a read on the file descriptor, returning the data in the buffer + if no data was read in the specified time an error is returned */ +int tio_read(TFILE *fp, void *buf, size_t count) +{ + struct timespec deadline = {0, 0}; + int rv; + uint8_t *tmp; + size_t newsz; + size_t len; + /* have a more convenient storage type for the buffer */ + uint8_t *ptr = (uint8_t *)buf; + /* loop until we have returned all the needed data */ + while (1) + { + /* check if we have enough data in the buffer */ + if (fp->readbuffer.len >= count) + { + if (count > 0) + { + if (ptr != NULL) + memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start, count); + /* adjust buffer position */ + fp->readbuffer.start += count; + fp->readbuffer.len -= count; + } + return 0; + } + /* empty what we have and continue from there */ + if (fp->readbuffer.len > 0) + { + if (ptr != NULL) + { + memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start, + fp->readbuffer.len); + ptr += fp->readbuffer.len; + } + count -= fp->readbuffer.len; + fp->readbuffer.start += fp->readbuffer.len; + fp->readbuffer.len = 0; + } + /* after this point until the read fp->readbuffer.len is 0 */ + if (!fp->read_resettable) + { + /* the stream is not resettable, re-use the buffer */ + fp->readbuffer.start = 0; + } + else if (fp->readbuffer.start >= (fp->readbuffer.size - 4)) + { + /* buffer is running empty, try to grow buffer */ + if (fp->readbuffer.size < fp->readbuffer.maxsize) + { + newsz = fp->readbuffer.size * 2; + if (newsz > fp->readbuffer.maxsize) + newsz = fp->readbuffer.maxsize; + tmp = realloc(fp->readbuffer.buffer, newsz); + if (tmp != NULL) + { + fp->readbuffer.buffer = tmp; + fp->readbuffer.size = newsz; + } + } + /* if buffer still does not contain enough room, clear resettable */ + if (fp->readbuffer.start >= (fp->readbuffer.size - 4)) + { + fp->readbuffer.start = 0; + fp->read_resettable = 0; + } + } + /* wait until we have input */ + if (tio_wait(fp->fd, POLLIN, fp->readtimeout, &deadline)) + return -1; + /* read the input in the buffer */ + len = fp->readbuffer.size - fp->readbuffer.start; +#ifdef SSIZE_MAX + if (len > SSIZE_MAX) + len = SSIZE_MAX; +#endif /* SSIZE_MAX */ + rv = read(fp->fd, fp->readbuffer.buffer + fp->readbuffer.start, len); + /* check for errors */ + if (rv == 0) + { + errno = ECONNRESET; + return -1; + } + else if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN)) + return -1; /* something went wrong with the read */ + else if (rv > 0) + fp->readbuffer.len = rv; /* skip the read part in the buffer */ +#ifdef DEBUG_TIO_STATS + fp->bytesread += rv; +#endif /* DEBUG_TIO_STATS */ + } +} + +/* Read and discard the specified number of bytes from the stream. */ +int tio_skip(TFILE *fp, size_t count) +{ + return tio_read(fp, NULL, count); +} + +/* Read all available data from the stream and empty the read buffer. */ +int tio_skipall(TFILE *fp, int timeout) +{ + struct timespec deadline = {0, 0}; + int rv; + size_t len; + /* clear the read buffer */ + fp->readbuffer.start = 0; + fp->readbuffer.len = 0; + fp->read_resettable = 0; + /* read until we can't read no more */ + len = fp->readbuffer.size; +#ifdef SSIZE_MAX + if (len > SSIZE_MAX) + len = SSIZE_MAX; +#endif /* SSIZE_MAX */ + while (1) + { + /* wait until we have input */ + if (tio_wait(fp->fd, POLLIN, timeout, &deadline)) + return -1; + /* read data from the stream */ + rv = read(fp->fd, fp->readbuffer.buffer, len); + if (rv == 0) + return 0; /* end-of-file */ + if ((rv < 0) && (errno == EWOULDBLOCK)) + return 0; /* we've ready everything we can without blocking */ + if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN)) + return -1; /* something went wrong with the read */ + } +} + +/* the caller has assured us that we can write to the file descriptor + and we give it a shot */ +static int tio_writebuf(TFILE *fp) +{ + int rv; + /* write the buffer */ +#ifdef MSG_NOSIGNAL + rv = send(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start, + fp->writebuffer.len, MSG_NOSIGNAL); +#else /* not MSG_NOSIGNAL */ + /* on platforms that cannot use send() with masked signals, we change the + signal mask and change it back after the write (note that there is a + race condition here) */ + struct sigaction act, oldact; + /* set up sigaction */ + memset(&act, 0, sizeof(struct sigaction)); + act.sa_sigaction = NULL; + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + /* ignore SIGPIPE */ + if (sigaction(SIGPIPE, &act, &oldact) != 0) + return -1; /* error setting signal handler */ + /* write the buffer */ + rv = write(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start, + fp->writebuffer.len); + /* restore the old handler for SIGPIPE */ + if (sigaction(SIGPIPE, &oldact, NULL) != 0) + return -1; /* error restoring signal handler */ +#endif + /* check for errors */ + if ((rv == 0) || ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))) + return -1; /* something went wrong with the write */ + /* skip the written part in the buffer */ + if (rv > 0) + { + fp->writebuffer.start += rv; + fp->writebuffer.len -= rv; +#ifdef DEBUG_TIO_STATS + fp->byteswritten += rv; +#endif /* DEBUG_TIO_STATS */ + /* reset start if len is 0 */ + if (fp->writebuffer.len == 0) + fp->writebuffer.start = 0; + /* move contents of the buffer to the front if it will save enough room */ + if (fp->writebuffer.start >= (fp->writebuffer.size / 4)) + { + memmove(fp->writebuffer.buffer, + fp->writebuffer.buffer + fp->writebuffer.start, + fp->writebuffer.len); + fp->writebuffer.start = 0; + } + } + return 0; +} + +/* write all the data in the buffer to the stream */ +int tio_flush(TFILE *fp) +{ + struct timespec deadline = {0, 0}; + /* loop until we have written our buffer */ + while (fp->writebuffer.len > 0) + { + /* wait until we can write */ + if (tio_wait(fp->fd, POLLOUT, fp->writetimeout, &deadline)) + return -1; + /* write one block */ + if (tio_writebuf(fp)) + return -1; + } + return 0; +} + +/* try a single write of data in the buffer if the file descriptor + will accept data */ +static int tio_flush_nonblock(TFILE *fp) +{ + struct pollfd fds[1]; + int rv; + /* see if we can write without blocking */ + fds[0].fd = fp->fd; + fds[0].events = POLLOUT; + rv = poll(fds, 1, 0); + /* check if any file descriptors were ready (timeout) or we were + interrupted */ + if ((rv == 0) || ((rv < 0) && ((errno == EINTR) || (errno == EAGAIN)))) + return 0; + /* any other errors? */ + if (rv < 0) + return -1; + /* so file descriptor will accept writes */ + return tio_writebuf(fp); +} + +int tio_write(TFILE *fp, const void *buf, size_t count) +{ + size_t fr; + uint8_t *tmp; + size_t newsz; + const uint8_t *ptr = (const uint8_t *)buf; + /* keep filling the buffer until we have bufferred everything */ + while (count > 0) + { + /* figure out free size in buffer */ + fr = fp->writebuffer.size - (fp->writebuffer.start + fp->writebuffer.len); + if (count <= fr) + { + /* the data fits in the buffer */ + memcpy(fp->writebuffer.buffer + fp->writebuffer.start + + fp->writebuffer.len, ptr, count); + fp->writebuffer.len += count; + return 0; + } + else if (fr > 0) + { + /* fill the buffer with data that will fit */ + memcpy(fp->writebuffer.buffer + fp->writebuffer.start + + fp->writebuffer.len, ptr, fr); + fp->writebuffer.len += fr; + ptr += fr; + count -= fr; + } + /* try to flush some of the data that is in the buffer */ + if (tio_flush_nonblock(fp)) + return -1; + /* if we have room now, try again */ + if (fp->writebuffer.size > (fp->writebuffer.start + fp->writebuffer.len)) + continue; + /* try to grow the buffer */ + if (fp->writebuffer.size < fp->writebuffer.maxsize) + { + newsz = fp->writebuffer.size * 2; + if (newsz > fp->writebuffer.maxsize) + newsz = fp->writebuffer.maxsize; + tmp = realloc(fp->writebuffer.buffer, newsz); + if (tmp != NULL) + { + fp->writebuffer.buffer = tmp; + fp->writebuffer.size = newsz; + continue; /* try again */ + } + } + /* write the buffer to the stream */ + if (tio_flush(fp)) + return -1; + } + return 0; +} + +int tio_close(TFILE *fp) +{ + int retv; + /* write any buffered data */ + retv = tio_flush(fp); +#ifdef DEBUG_TIO_STATS + /* dump statistics to stderr */ + fprintf(stderr, "DEBUG_TIO_STATS READ=%d WRITTEN=%d\n", fp->bytesread, + fp->byteswritten); +#endif /* DEBUG_TIO_STATS */ + /* close file descriptor */ + if (close(fp->fd)) + retv = -1; + /* free any allocated buffers */ + memset(fp->readbuffer.buffer, 0, fp->readbuffer.size); + memset(fp->writebuffer.buffer, 0, fp->writebuffer.size); + free(fp->readbuffer.buffer); + free(fp->writebuffer.buffer); + /* free the tio struct itself */ + free(fp); + /* return the result of the earlier operations */ + return retv; +} + +void tio_mark(TFILE *fp) +{ + /* move any data in the buffer to the start of the buffer */ + if ((fp->readbuffer.start > 0) && (fp->readbuffer.len > 0)) + { + memmove(fp->readbuffer.buffer, + fp->readbuffer.buffer + fp->readbuffer.start, fp->readbuffer.len); + fp->readbuffer.start = 0; + } + /* mark the stream as resettable */ + fp->read_resettable = 1; +} + +int tio_reset(TFILE *fp) +{ + /* check if the stream is (still) resettable */ + if (!fp->read_resettable) + return -1; + /* reset the buffer */ + fp->readbuffer.len += fp->readbuffer.start; + fp->readbuffer.start = 0; + return 0; +} diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h new file mode 100644 index 0000000..95f9812 --- /dev/null +++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h @@ -0,0 +1,83 @@ +/* + tio.h - timed io functions + This file is part of the nss-pam-ldapd library. + + Copyright (C) 2007, 2008, 2010, 2012, 2013 Arthur de Jong + + 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 +*/ + +/* + + TODO: Add some documentation here. + + the SIGPIPE signal should be ignored (is ignored in this code) + + This library is not thread safe. You cannot share TFILE objects between + threads and expect to be able to read and write from them in different + threads. All the state is in the TFILE object so calls to this library on + different objects can be done in parallel. + +*/ + +#ifndef COMMON__TIO_H +#define COMMON__TIO_H + +#include <sys/time.h> +#include <sys/types.h> + +#include "attrs.h" + +/* This is a generic file handle used for reading and writing + (something like FILE from stdio.h). */ +typedef struct tio_fileinfo TFILE; + +/* Open a new TFILE based on the file descriptor. The timeout is set for any + operation (value in milliseconds). */ +TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout, + size_t initreadsize, size_t maxreadsize, + size_t initwritesize, size_t maxwritesize) + LIKE_MALLOC MUST_USE; + +/* Read the specified number of bytes from the stream. */ +int tio_read(TFILE *fp, void *buf, size_t count); + +/* Read and discard the specified number of bytes from the stream. */ +int tio_skip(TFILE *fp, size_t count); + +/* Read all available data from the stream and empty the read buffer. */ +int tio_skipall(TFILE *fp, int timeout); + +/* Write the specified buffer to the stream. */ +int tio_write(TFILE *fp, const void *buf, size_t count); + +/* Write out all buffered data to the stream. */ +int tio_flush(TFILE *fp); + +/* Flush the streams and closes the underlying file descriptor. */ +int tio_close(TFILE *fp); + +/* Store the current position in the stream so that we can jump back to it + with the tio_reset() function. */ +void tio_mark(TFILE *fp); + +/* Rewinds the stream to the point set by tio_mark(). Note that this only + resets the read stream and not the write stream. This function returns + whether the reset was successful (this function may fail if the buffers + were full). */ +int tio_reset(TFILE *fp); + +#endif /* COMMON__TIO_H */ diff --git a/contrib/slapd-modules/nssov/nssov.c b/contrib/slapd-modules/nssov/nssov.c new file mode 100644 index 0000000..f024cc5 --- /dev/null +++ b/contrib/slapd-modules/nssov/nssov.c @@ -0,0 +1,993 @@ +/* nssov.c - nss-ldap overlay for slapd */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 by Howard Chu, Symas Corp. + * Portions Copyright 2013 by Ted C. Cheng, 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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +#ifndef SLAPD_OVER_NSSOV +#define SLAPD_OVER_NSSOV SLAPD_MOD_DYNAMIC +#endif + +#include "../slapd/config.h" /* not nss-ldapd config.h */ + +#include "lutil.h" + +#include <ac/errno.h> +#include <ac/unistd.h> +#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 +#define WRITEBUFFER_MINSIZE 64 +#define WRITEBUFFER_MAXSIZE 64*1024 + +/* Find the given attribute's value in the RDN of the DN */ +void nssov_find_rdnval(struct berval *dn, AttributeDescription *ad, struct berval *value) +{ + struct berval rdn; + char *next; + + BER_BVZERO(value); + dnRdn( dn, &rdn ); + do { + next = ber_bvchr( &rdn, '+' ); + if ( rdn.bv_val[ad->ad_cname.bv_len] == '=' && + !ber_bvcmp( &rdn, &ad->ad_cname )) { + if ( next ) + rdn.bv_len = next - rdn.bv_val; + value->bv_val = rdn.bv_val + ad->ad_cname.bv_len + 1; + value->bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1; + break; + } + if ( !next ) + break; + next++; + rdn.bv_len -= next - rdn.bv_val; + rdn.bv_val = next; + } while (1); +} + +/* create a search filter using a name that requires escaping */ +int nssov_filter_byname(nssov_mapinfo *mi,int key,struct berval *name,struct berval *buf) +{ + char buf2[1024]; + struct berval bv2 = {sizeof(buf2),buf2}; + + /* escape attribute */ + if (nssov_escape(name,&bv2)) + return -1; + /* build filter */ + if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 > + buf->bv_len ) + return -1; + buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))", + mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val, + bv2.bv_val ); + return 0; +} + +/* create a search filter using a string converted from an int */ +int nssov_filter_byid(nssov_mapinfo *mi,int key,struct berval *id,struct berval *buf) +{ + /* build filter */ + if (id->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 > + buf->bv_len ) + return -1; + buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))", + mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val, + id->bv_val ); + return 0; +} + +void get_userpassword(struct berval *attr,struct berval *pw) +{ + int i; + /* go over the entries and return the remainder of the value if it + starts with {crypt} or crypt$ */ + for (i=0;!BER_BVISNULL(&attr[i]);i++) + { + if (strncasecmp(attr[i].bv_val,"{crypt}",7)==0) { + pw->bv_val = attr[i].bv_val + 7; + pw->bv_len = attr[i].bv_len - 7; + return; + } + if (strncasecmp(attr[i].bv_val,"crypt$",6)==0) { + pw->bv_val = attr[i].bv_val + 6; + pw->bv_len = attr[i].bv_len - 6; + return; + } + } + /* just return the first value completely */ + *pw = *attr; + /* TODO: support more password formats e.g. SMD5 + (which is $1$ but in a different format) + (any code for this is more than welcome) */ +} + +/* this writes a single address to the stream */ +int write_address(TFILE *fp,struct berval *addr) +{ + int32_t tmpint32; + struct in_addr ipv4addr; + struct in6_addr ipv6addr; + /* try to parse the address as IPv4 first, fall back to IPv6 */ + if (inet_pton(AF_INET,addr->bv_val,&ipv4addr)>0) + { + /* write address type */ + WRITE_INT32(fp,AF_INET); + /* write the address length */ + WRITE_INT32(fp,sizeof(struct in_addr)); + /* write the address itself (in network byte order) */ + WRITE(fp,&ipv4addr,sizeof(struct in_addr)); + } + else if (inet_pton(AF_INET6,addr->bv_val,&ipv6addr)>0) + { + /* write address type */ + WRITE_INT32(fp,AF_INET6); + /* write the address length */ + WRITE_INT32(fp,sizeof(struct in6_addr)); + /* write the address itself (in network byte order) */ + WRITE(fp,&ipv6addr,sizeof(struct in6_addr)); + } + else + { + /* 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\n",addr->bv_val,0,0); + /* write an illegal address type */ + WRITE_INT32(fp,-1); + /* write an empty address */ + WRITE_INT32(fp,0); + } + /* we're done */ + return 0; +} + +int read_address(TFILE *fp,char *addr,int *addrlen,int *af) +{ + int32_t tmpint32; + int len; + /* read address family */ + READ_INT32(fp,*af); + if ((*af!=AF_INET)&&(*af!=AF_INET6)) + { + 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\n",len,0,0); + return -1; + } + *addrlen=len; + /* read address */ + READ(fp,addr,len); + /* we're done */ + return 0; +} + +int nssov_escape(struct berval *src,struct berval *dst) +{ + size_t pos=0; + int i; + /* go over all characters in source string */ + for (i=0;i<src->bv_len;i++) + { + /* check if char will fit */ + if (pos>=(dst->bv_len-4)) + return -1; + /* do escaping for some characters */ + switch (src->bv_val[i]) + { + case '*': + strcpy(dst->bv_val+pos,"\\2a"); + pos+=3; + break; + case '(': + strcpy(dst->bv_val+pos,"\\28"); + pos+=3; + break; + case ')': + strcpy(dst->bv_val+pos,"\\29"); + pos+=3; + break; + case '\\': + strcpy(dst->bv_val+pos,"\\5c"); + pos+=3; + break; + default: + /* just copy character */ + dst->bv_val[pos++]=src->bv_val[i]; + break; + } + } + /* terminate destination string */ + dst->bv_val[pos]='\0'; + dst->bv_len = pos; + return 0; +} + +/* read the version information and action from the stream + this function returns the read action in location pointer to by action */ +static int read_header(TFILE *fp,int32_t *action) +{ + int32_t tmpint32; + /* read the protocol version */ + READ_INT32(fp,tmpint32); + if (tmpint32 != (int32_t)NSLCD_VERSION) + { + Debug( LDAP_DEBUG_TRACE,"nssov: wrong nslcd version id (%d)\n",(int)tmpint32,0,0); + return -1; + } + /* read the request type */ + READ_INT32(fp,*action); + return 0; +} + +int nssov_config(nssov_info *ni,TFILE *fp,Operation *op) +{ + int opt; + int32_t tmpint32; + + READ_INT32(fp,opt); + + Debug(LDAP_DEBUG_TRACE, "nssov_config (%d)\n",opt,0,0); + + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_CONFIG_GET); + WRITE_INT32(fp,NSLCD_RESULT_BEGIN); + + switch (opt) { + case NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE: + /* request for pam password_prohibit_message */ + /* nssov_pam prohibits password */ + if (!BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) { + Debug(LDAP_DEBUG_TRACE,"nssov_config(): %s (%s)\n", + "password_prohibit_message", + ni->ni_pam_password_prohibit_message.bv_val,0); + WRITE_STRING(fp,ni->ni_pam_password_prohibit_message.bv_val); + } + default: + /* all other config options are ignored */ + break; + } + + WRITE_INT32(fp,NSLCD_RESULT_END); + return 0; +} + + +/* read a request message, returns <0 in case of errors, + this function closes the socket */ +static void handleconnection(nssov_info *ni,int sock,Operation *op) +{ + TFILE *fp; + int32_t action; + int readtimeout,writetimeout; + uid_t uid; + gid_t gid; + char authid[sizeof("gidNumber=4294967295+uidNumber=424967295,cn=peercred,cn=external,cn=auth")]; + char peerbuf[8]; + struct berval peerbv = { sizeof(peerbuf), peerbuf }; + + /* log connection */ + if (LUTIL_GETPEEREID(sock,&uid,&gid,&peerbv)) + 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\n", + (int)uid,(int)gid,0); + + /* Should do authid mapping too */ + op->o_dn.bv_len = sprintf(authid,"gidNumber=%d+uidNumber=%d,cn=peercred,cn=external,cn=auth", + (int)gid, (int)uid ); + op->o_dn.bv_val = authid; + op->o_ndn = op->o_dn; + + /* set the timeouts: + * read timeout is half a second because clients should send their request + * quickly, write timeout is 60 seconds because clients could be taking some + * time to process the results + */ + readtimeout = 500; + writetimeout = 60000; + /* create a stream object */ + if ((fp=tio_fdopen(sock,readtimeout,writetimeout, + READBUFFER_MINSIZE,READBUFFER_MAXSIZE, + WRITEBUFFER_MINSIZE,WRITEBUFFER_MAXSIZE))==NULL) + { + Debug( LDAP_DEBUG_ANY,"nssov: cannot create stream for writing: %s",strerror(errno),0,0); + (void)close(sock); + return; + } + /* read request */ + if (read_header(fp,&action)) + { + (void)tio_close(fp); + return; + } + /* handle request */ + switch (action) + { + case NSLCD_ACTION_ALIAS_BYNAME: (void)nssov_alias_byname(ni,fp,op); break; + case NSLCD_ACTION_ALIAS_ALL: (void)nssov_alias_all(ni,fp,op); break; + case NSLCD_ACTION_ETHER_BYNAME: (void)nssov_ether_byname(ni,fp,op); break; + case NSLCD_ACTION_ETHER_BYETHER: (void)nssov_ether_byether(ni,fp,op); break; + case NSLCD_ACTION_ETHER_ALL: (void)nssov_ether_all(ni,fp,op); break; + case NSLCD_ACTION_GROUP_BYNAME: (void)nssov_group_byname(ni,fp,op); break; + case NSLCD_ACTION_GROUP_BYGID: (void)nssov_group_bygid(ni,fp,op); break; + case NSLCD_ACTION_GROUP_BYMEMBER: (void)nssov_group_bymember(ni,fp,op); break; + case NSLCD_ACTION_GROUP_ALL: (void)nssov_group_all(ni,fp,op); break; + case NSLCD_ACTION_HOST_BYNAME: (void)nssov_host_byname(ni,fp,op); break; + case NSLCD_ACTION_HOST_BYADDR: (void)nssov_host_byaddr(ni,fp,op); break; + case NSLCD_ACTION_HOST_ALL: (void)nssov_host_all(ni,fp,op); break; + case NSLCD_ACTION_NETGROUP_BYNAME: (void)nssov_netgroup_byname(ni,fp,op); break; + case NSLCD_ACTION_NETWORK_BYNAME: (void)nssov_network_byname(ni,fp,op); break; + case NSLCD_ACTION_NETWORK_BYADDR: (void)nssov_network_byaddr(ni,fp,op); break; + case NSLCD_ACTION_NETWORK_ALL: (void)nssov_network_all(ni,fp,op); break; + case NSLCD_ACTION_PASSWD_BYNAME: (void)nssov_passwd_byname(ni,fp,op); break; + case NSLCD_ACTION_PASSWD_BYUID: (void)nssov_passwd_byuid(ni,fp,op); break; + case NSLCD_ACTION_PASSWD_ALL: (void)nssov_passwd_all(ni,fp,op); break; + case NSLCD_ACTION_PROTOCOL_BYNAME: (void)nssov_protocol_byname(ni,fp,op); break; + case NSLCD_ACTION_PROTOCOL_BYNUMBER:(void)nssov_protocol_bynumber(ni,fp,op); break; + case NSLCD_ACTION_PROTOCOL_ALL: (void)nssov_protocol_all(ni,fp,op); break; + case NSLCD_ACTION_RPC_BYNAME: (void)nssov_rpc_byname(ni,fp,op); break; + case NSLCD_ACTION_RPC_BYNUMBER: (void)nssov_rpc_bynumber(ni,fp,op); break; + case NSLCD_ACTION_RPC_ALL: (void)nssov_rpc_all(ni,fp,op); break; + case NSLCD_ACTION_SERVICE_BYNAME: (void)nssov_service_byname(ni,fp,op); break; + case NSLCD_ACTION_SERVICE_BYNUMBER: (void)nssov_service_bynumber(ni,fp,op); break; + 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,uid); 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,uid); break; + case NSLCD_ACTION_CONFIG_GET: (void)nssov_config(ni,fp,op); break; + default: + Debug( LDAP_DEBUG_ANY,"nssov: invalid request id: %d",(int)action,0,0); + break; + } + /* we're done with the request */ + (void)tio_close(fp); + return; +} + +/* accept a connection on the socket */ +static void *acceptconn(void *ctx, void *arg) +{ + nssov_info *ni = arg; + Connection conn = {0}; + OperationBuffer opbuf; + Operation *op; + int csock; + + if ( slapd_shutdown ) + return NULL; + + { + struct sockaddr_storage addr; + socklen_t alen; + int j; + + /* accept a new connection */ + alen=(socklen_t)sizeof(struct sockaddr_storage); + csock=accept(ni->ni_socket,(struct sockaddr *)&addr,&alen); + connection_client_enable(ni->ni_conn); + if (csock<0) + { + if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK)) + { + Debug( LDAP_DEBUG_TRACE,"nssov: accept() failed (ignored): %s",strerror(errno),0,0); + return NULL; + } + Debug( LDAP_DEBUG_ANY,"nssov: accept() failed: %s",strerror(errno),0,0); + return NULL; + } + /* make sure O_NONBLOCK is not inherited */ + if ((j=fcntl(csock,F_GETFL,0))<0) + { + Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_GETFL) failed: %s",strerror(errno),0,0); + if (close(csock)) + Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0); + return NULL; + } + if (fcntl(csock,F_SETFL,j&~O_NONBLOCK)<0) + { + Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,~O_NONBLOCK) failed: %s",strerror(errno),0,0); + if (close(csock)) + Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0); + return NULL; + } + } + 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; + + /* handle the connection */ + handleconnection(ni,csock,op); + + return NULL; +} + +static slap_verbmasks nss_svcs[] = { + { BER_BVC("aliases"), NM_alias }, + { BER_BVC("ethers"), NM_ether }, + { BER_BVC("group"), NM_group }, + { BER_BVC("hosts"), NM_host }, + { BER_BVC("netgroup"), NM_netgroup }, + { BER_BVC("networks"), NM_network }, + { BER_BVC("passwd"), NM_passwd }, + { BER_BVC("protocols"), NM_protocol }, + { BER_BVC("rpc"), NM_rpc }, + { BER_BVC("services"), NM_service }, + { BER_BVC("shadow"), NM_shadow }, + { 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_PAM, + NSS_PAMGROUP, + NSS_PAMSESS +}; + +static ConfigDriver nss_cf_gen; + +static ConfigTable nsscfg[] = { + { "nssov-ssd", "service> <url", 3, 3, 0, ARG_MAGIC|NSS_SSD, + nss_cf_gen, "(OLcfgCtAt:3.1 NAME 'olcNssSsd' " + "DESC 'URL for searches in a given service' " + "EQUALITY caseIgnoreMatch " + "SYNTAX OMsDirectoryString )", NULL, NULL }, + { "nssov-map", "service> <orig> <new", 4, 4, 0, ARG_MAGIC|NSS_MAP, + nss_cf_gen, "(OLcfgCtAt:3.2 NAME 'olcNssMap' " + "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|NSS_PAMSESS, + nss_cf_gen, "(OLcfgCtAt:3.11 NAME 'olcNssPamSession' " + "DESC 'Services for which sessions will be recorded' " + "EQUALITY caseIgnoreMatch " + "SYNTAX OMsDirectoryString )", NULL, NULL }, + { "nssov-pam-password-prohibit-message", + "password_prohibit_message", 2, 2, 0, + ARG_OFFSET|ARG_BERVAL, + (void *)offsetof(struct nssov_info, ni_pam_password_prohibit_message), + "(OLcfgCtAt:3.12 NAME 'olcNssPamPwdProhibitMsg' " + "DESC 'Prohibit password modification message' " + "EQUALITY caseIgnoreMatch " + "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, + { "nssov-pam-pwdmgr-dn", + "pwdmgr_dn", 2, 2, 0, + ARG_OFFSET|ARG_BERVAL, + (void *)offsetof(struct nssov_info, ni_pam_pwdmgr_dn), + "(OLcfgCtAt:3.13 NAME 'olcPamPwdmgrDn' " + "DESC 'Password Manager DN' " + "EQUALITY distinguishedNameMatch " + "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL }, + { "nssov-pam-pwdmgr-pwd", + "pwdmgr_pwd", 2, 2, 0, + ARG_OFFSET|ARG_BERVAL, + (void *)offsetof(struct nssov_info, ni_pam_pwdmgr_pwd), + "(OLcfgCtAt:3.14 NAME 'olcPamPwdmgrPwd' " + "DESC 'Password Manager Pwd' " + "EQUALITY octetStringMatch " + "SYNTAX OMsOctetString SINGLE-VALUE )", NULL, NULL }, + { NULL, NULL, 0,0,0, ARG_IGNORED } +}; + +static ConfigOCs nssocs[] = { + { "( OLcfgCtOc:3.1 " + "NAME 'olcNssOvConfig' " + "DESC 'NSS lookup configuration' " + "SUP olcOverlayConfig " + "MAY ( olcNssSsd $ olcNssMap $ olcNssPam $ olcNssPamDefHost $ " + "olcNssPamGroupDN $ olcNssPamGroupAD $ " + "olcNssPamMinUid $ olcNssPamMaxUid $ olcNssPamSession $ " + "olcNssPamTemplateAD $ olcNssPamTemplate ) )", + Cft_Overlay, nsscfg }, + { NULL, 0, NULL } +}; + +static int +nss_cf_gen(ConfigArgs *c) +{ + slap_overinst *on = (slap_overinst *)c->bi; + 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) { + case NSS_SSD: + rc = 1; + for (i=NM_alias;i<NM_NONE;i++) { + struct berval scope; + struct berval ssd; + struct berval base; + + mi = &ni->ni_maps[i]; + + /* ignore all-default services */ + if ( mi->mi_scope == LDAP_SCOPE_DEFAULT && + bvmatch( &mi->mi_filter, &mi->mi_filter0 ) && + BER_BVISNULL( &mi->mi_base )) + continue; + + if ( BER_BVISNULL( &mi->mi_base )) + base = ni->ni_db->be_nsuffix[0]; + else + base = mi->mi_base; + ldap_pvt_scope2bv(mi->mi_scope == LDAP_SCOPE_DEFAULT ? + LDAP_SCOPE_SUBTREE : mi->mi_scope, &scope); + ssd.bv_len = STRLENOF(" ldap:///???") + nss_svcs[i].word.bv_len + + base.bv_len + scope.bv_len + mi->mi_filter.bv_len; + ssd.bv_val = ch_malloc( ssd.bv_len + 1 ); + sprintf(ssd.bv_val, "%s ldap:///%s??%s?%s", nss_svcs[i].word.bv_val, + base.bv_val, scope.bv_val, mi->mi_filter.bv_val ); + ber_bvarray_add( &c->rvalue_vals, &ssd ); + rc = 0; + } + break; + case NSS_MAP: + rc = 1; + for (i=NM_alias;i<NM_NONE;i++) { + + mi = &ni->ni_maps[i]; + for (j=0;!BER_BVISNULL(&mi->mi_attrkeys[j]);j++) { + if ( ber_bvstrcasecmp(&mi->mi_attrkeys[j], + &mi->mi_attrs[j].an_name)) { + struct berval map; + + map.bv_len = nss_svcs[i].word.bv_len + + mi->mi_attrkeys[j].bv_len + + mi->mi_attrs[j].an_desc->ad_cname.bv_len + 2; + map.bv_val = ch_malloc(map.bv_len + 1); + sprintf(map.bv_val, "%s %s %s", nss_svcs[i].word.bv_val, + mi->mi_attrkeys[j].bv_val, mi->mi_attrs[j].an_desc->ad_cname.bv_val ); + ber_bvarray_add( &c->rvalue_vals, &map ); + rc = 0; + } + } + } + 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 ) { + case NSS_SSD: { + LDAPURLDesc *lud; + + i = verb_to_mask(c->argv[1], nss_svcs); + if ( i == NM_NONE ) + return 1; + + mi = &ni->ni_maps[i]; + rc = ldap_url_parse(c->argv[2], &lud); + if ( rc ) + return 1; + do { + struct berval base; + /* Must be LDAP scheme */ + if (strcasecmp(lud->lud_scheme,"ldap")) { + rc = 1; + break; + } + /* Host part, attrs, and extensions must be empty */ + if (( lud->lud_host && *lud->lud_host ) || + lud->lud_attrs || lud->lud_exts ) { + rc = 1; + break; + } + ber_str2bv( lud->lud_dn,0,0,&base); + rc = dnNormalize( 0,NULL,NULL,&base,&mi->mi_base,NULL); + if ( rc ) + break; + if ( lud->lud_filter ) { + /* steal this */ + ber_str2bv( lud->lud_filter,0,0,&mi->mi_filter); + lud->lud_filter = NULL; + } + mi->mi_scope = lud->lud_scope; + } while(0); + ldap_free_urldesc( lud ); + } + break; + case NSS_MAP: + i = verb_to_mask(c->argv[1], nss_svcs); + if ( i == NM_NONE ) + return 1; + rc = 1; + mi = &ni->ni_maps[i]; + for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) { + if (!strcasecmp(c->argv[2],mi->mi_attrkeys[j].bv_val)) { + AttributeDescription *ad = NULL; + const char *text; + rc = slap_str2ad( c->argv[3], &ad, &text); + if ( rc == 0 ) { + mi->mi_attrs[j].an_desc = ad; + mi->mi_attrs[j].an_name = ad->ad_cname; + } + break; + } + } + 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_str2bv( c->argv[1], 0, 1, &c->value_bv ); + ber_bvarray_add( &ni->ni_pam_sessions, &c->value_bv ); + break; + } + return rc; +} + +static int +nssov_db_init( + BackendDB *be, + ConfigReply *cr ) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + nssov_info *ni; + int rc; + + 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_alias_init(ni); + nssov_ether_init(ni); + nssov_group_init(ni); + nssov_host_init(ni); + nssov_netgroup_init(ni); + nssov_network_init(ni); + nssov_passwd_init(ni); + nssov_protocol_init(ni); + nssov_rpc_init(ni); + nssov_service_init(ni); + nssov_shadow_init(ni); + + ni->ni_db = be->bd_self; + ni->ni_pam_opts = NI_PAM_UID2DN; + + return 0; +} + +static int +nssov_db_destroy( + BackendDB *be, + ConfigReply *cr ) +{ + return 0; +} + +static int +nssov_db_open( + BackendDB *be, + ConfigReply *cr ) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + nssov_info *ni = on->on_bi.bi_private; + nssov_mapinfo *mi; + + int i, sock; + struct sockaddr_un addr; + + /* Set default bases */ + for (i=0; i<NM_NONE; i++) { + if ( BER_BVISNULL( &ni->ni_maps[i].mi_base )) { + ber_dupbv( &ni->ni_maps[i].mi_base, &be->be_nsuffix[0] ); + } + if ( ni->ni_maps[i].mi_scope == LDAP_SCOPE_DEFAULT ) + ni->ni_maps[i].mi_scope = LDAP_SCOPE_SUBTREE; + } + /* validate attribute maps */ + mi = ni->ni_maps; + for ( i=0; i<NM_NONE; i++,mi++) { + const char *text; + int j; + for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) { + /* skip attrs we already validated */ + if ( mi->mi_attrs[j].an_desc ) continue; + if ( slap_bv2ad( &mi->mi_attrs[j].an_name, + &mi->mi_attrs[j].an_desc, &text )) { + Debug(LDAP_DEBUG_ANY,"nssov: invalid attr \"%s\": %s\n", + mi->mi_attrs[j].an_name.bv_val, text, 0 ); + return -1; + } + } + BER_BVZERO(&mi->mi_attrs[j].an_name); + 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\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\n", + strerror(errno),0,0); + } + /* create socket address structure */ + memset(&addr,0,sizeof(struct sockaddr_un)); + addr.sun_family=AF_UNIX; + strncpy(addr.sun_path,NSLCD_SOCKET,sizeof(addr.sun_path)); + addr.sun_path[sizeof(addr.sun_path)-1]='\0'; + /* bind to the named socket */ + if (bind(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr_un))) + { + Debug( LDAP_DEBUG_ANY,"nssov: bind() to "NSLCD_SOCKET" failed: %s", + strerror(errno),0,0); + if (close(sock)) + Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0); + return -1; + } + /* close the file descriptor on exit */ + if (fcntl(sock,F_SETFD,FD_CLOEXEC)<0) + { + Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,O_NONBLOCK) failed: %s",strerror(errno),0,0); + if (close(sock)) + Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0); + return -1; + } + /* set permissions of socket so anybody can do requests */ + /* Note: we use chmod() here instead of fchmod() because + fchmod does not work on sockets + http://www.opengroup.org/onlinepubs/009695399/functions/fchmod.html + http://lkml.org/lkml/2005/5/16/11 */ + if (chmod(NSLCD_SOCKET,(mode_t)0666)) + { + Debug( LDAP_DEBUG_ANY,"nssov: chmod(0666) failed: %s",strerror(errno),0,0); + if (close(sock)) + Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0); + return -1; + } + /* start listening for connections */ + if (listen(sock,SOMAXCONN)<0) + { + Debug( LDAP_DEBUG_ANY,"nssov: listen() failed: %s",strerror(errno),0,0); + if (close(sock)) + Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0); + return -1; + } + ni->ni_socket = sock; + ni->ni_conn = connection_client_setup( sock, acceptconn, ni ); + } + + return 0; +} + +static int +nssov_db_close( + BackendDB *be, + ConfigReply *cr ) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + nssov_info *ni = on->on_bi.bi_private; + + if ( slapMode & SLAP_SERVER_MODE ) { + /* close socket if it's still in use */ + if (ni->ni_socket >= 0); + { + if (close(ni->ni_socket)) + Debug( LDAP_DEBUG_ANY,"problem closing server socket (ignored): %s",strerror(errno),0,0); + ni->ni_socket = -1; + } + /* remove existing named socket */ + if (unlink(NSLCD_SOCKET)<0) + { + Debug( LDAP_DEBUG_TRACE,"unlink() of "NSLCD_SOCKET" failed (ignored): %s", + strerror(errno),0,0); + } + } + return 0; +} + +static slap_overinst nssov; + +int +nssov_initialize( void ) +{ + int rc; + + nssov.on_bi.bi_type = "nssov"; + nssov.on_bi.bi_db_init = nssov_db_init; + nssov.on_bi.bi_db_destroy = nssov_db_destroy; + nssov.on_bi.bi_db_open = nssov_db_open; + nssov.on_bi.bi_db_close = nssov_db_close; + + nssov.on_bi.bi_cf_ocs = nssocs; + + rc = config_register_schema( nsscfg, nssocs ); + if ( rc ) return rc; + + return overlay_register(&nssov); +} + +#if SLAPD_OVER_NSSOV == SLAPD_MOD_DYNAMIC +int +init_module( int argc, char *argv[] ) +{ + return nssov_initialize(); +} +#endif diff --git a/contrib/slapd-modules/nssov/nssov.h b/contrib/slapd-modules/nssov/nssov.h new file mode 100644 index 0000000..8af6bfa --- /dev/null +++ b/contrib/slapd-modules/nssov/nssov.h @@ -0,0 +1,347 @@ +/* nssov.h - NSS overlay header file */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 Howard Chu. + * Portions Copyright 2013 Ted C. Cheng, 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>. + */ + +#ifndef NSSOV_H +#define NSSOV_H + +#ifndef NSLCD_PATH +#define NSLCD_PATH "/var/run/nslcd" +#endif + +#ifndef NSLCD_SOCKET +#define NSLCD_SOCKET NSLCD_PATH "/socket" +#endif + +#include <stdio.h> + +#include "nslcd.h" +#include "nslcd-prot.h" +#include "tio.h" +#include "attrs.h" + +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION + +#include "portable.h" +#include "slap.h" +#include <ac/string.h> + +/* selectors for different maps */ +enum nssov_map_selector +{ + NM_alias, + NM_ether, + NM_group, + NM_host, + NM_netgroup, + NM_network, + NM_passwd, + NM_protocol, + NM_rpc, + NM_service, + NM_shadow, + NM_NONE +}; + +typedef struct nssov_mapinfo { + struct berval mi_base; + int mi_scope; + struct berval mi_filter0; + struct berval mi_filter; + struct berval *mi_attrkeys; + AttributeName *mi_attrs; +} nssov_mapinfo; + +typedef struct nssov_info +{ + /* search timelimit */ + int ni_timelimit; + struct nssov_mapinfo ni_maps[NM_NONE]; + 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; + struct berval ni_pam_password_prohibit_message; + struct berval ni_pam_pwdmgr_dn; + struct berval ni_pam_pwdmgr_pwd; +} 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); + +/* macros for basic read and write operations, the following + ERROR_OUT* marcos define the action taken on errors + the stream is not closed because the caller closes the + stream */ + +#define ERROR_OUT_WRITEERROR(fp) \ + Debug(LDAP_DEBUG_ANY,"nssov: error writing to client\n",0,0,0); \ + return -1; + +#define ERROR_OUT_READERROR(fp) \ + Debug(LDAP_DEBUG_ANY,"nssov: error reading from client\n",0,0,0); \ + return -1; + +#define ERROR_OUT_BUFERROR(fp) \ + Debug(LDAP_DEBUG_ANY,"nssov: client supplied argument too large\n",0,0,0); \ + return -1; + +#define WRITE_BERVAL(fp, bv) \ + DEBUG_PRINT("WRITE_BERVAL: var="__STRING(bv)" bv_val=\"%s\"", (bv)->bv_val); \ + if ((bv) == NULL) \ + { \ + WRITE_INT32(fp, 0); \ + } \ + else \ + { \ + WRITE_INT32(fp, (bv)->bv_len); \ + tmpint32 = ntohl(tmpint32); \ + if (tmpint32 > 0) \ + { \ + WRITE(fp, (bv)->bv_val, tmpint32); \ + } \ + } \ + +#define WRITE_BVARRAY(fp, arr) \ + if ((arr) == NULL) \ + { \ + DEBUG_PRINT("WRITE_BVARRAY: var="__STRING(arr)" num=%d", 0); \ + WRITE_INT32(fp, 0); \ + } \ + else \ + { \ + /* first determine length of array */ \ + for (tmp3int32 = 0; (arr)[tmp3int32].bv_val != NULL; tmp3int32++) \ + /* nothing */ ; \ + /* write number of strings */ \ + DEBUG_PRINT("WRITE_BVARRAY: var="__STRING(arr)" num=%d", (int)tmp3int32); \ + WRITE_INT32(fp, tmp3int32); \ + /* write strings */ \ + for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \ + { \ + WRITE_BERVAL(fp, &(arr)[tmp2int32]); \ + } \ + } \ + +/* Find the given attribute's value in the RDN of the DN. */ +void nssov_find_rdnval(struct berval *dn,AttributeDescription *ad,struct berval *value); + +/* This tries to get the user password attribute from the entry. + It will try to return an encrypted password as it is used in /etc/passwd, + /etc/group or /etc/shadow depending upon what is in the directory. + This function will return NULL if no passwd is found and will return the + literal value in the directory if conversion is not possible. */ +void get_userpassword(struct berval *attr, struct berval *pw); + +/* write out an address, parsing the addr value */ +int write_address(TFILE *fp,struct berval *addr); + +/* a helper macro to write out addresses and bail out on errors */ +#define WRITE_ADDRESS(fp,addr) \ + if (write_address(fp,addr)) \ + return -1; + +/* read an address from the stream */ +int read_address(TFILE *fp,char *addr,int *addrlen,int *af); + +/* helper macro to read an address from the stream */ +#define READ_ADDRESS(fp,addr,len,af) \ + len=(int)sizeof(addr); \ + if (read_address(fp,addr,&(len),&(af))) \ + return -1; + +/* checks to see if the specified string is a valid username */ +int isvalidusername(struct berval *name); + +/* 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); + +int nssov_filter_byname(nssov_mapinfo *mi,int key,struct berval *name,struct berval *buf); +int nssov_filter_byid(nssov_mapinfo *mi,int key,struct berval *id,struct berval *buf); + +void nssov_alias_init(nssov_info *ni); +void nssov_ether_init(nssov_info *ni); +void nssov_group_init(nssov_info *ni); +void nssov_host_init(nssov_info *ni); +void nssov_netgroup_init(nssov_info *ni); +void nssov_network_init(nssov_info *ni); +void nssov_passwd_init(nssov_info *ni); +void nssov_protocol_init(nssov_info *ni); +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); +int nssov_alias_all(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_ether_byname(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_ether_byether(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_ether_all(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_group_byname(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_group_bygid(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_group_bymember(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_group_all(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_host_byname(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_host_byaddr(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_host_all(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_netgroup_byname(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_network_byname(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_network_byaddr(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_network_all(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_passwd_byname(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_passwd_byuid(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_passwd_all(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_protocol_byname(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_protocol_bynumber(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_protocol_all(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_rpc_byname(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_rpc_bynumber(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_rpc_all(nssov_info *ni,TFILE *fp,Operation *op); +int nssov_service_byname(nssov_info *ni,TFILE *fp,Operation *op); +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,uid_t calleruid); +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,uid_t calleruid); + +/* config initialization */ +#define NSSOV_INIT(db) \ + void nssov_##db##_init(nssov_info *ni) \ + { \ + nssov_mapinfo *mi = &ni->ni_maps[NM_##db]; \ + int i; \ + for (i=0;!BER_BVISNULL(&db##_keys[i]);i++); \ + i++; \ + mi->mi_attrs = ch_malloc( i*sizeof(AttributeName)); \ + for (i=0;!BER_BVISNULL(&db##_keys[i]);i++) { \ + mi->mi_attrs[i].an_name = db##_keys[i]; \ + mi->mi_attrs[i].an_desc = NULL; \ + } \ + mi->mi_scope = LDAP_SCOPE_DEFAULT; \ + mi->mi_filter0 = db##_filter; \ + ber_dupbv( &mi->mi_filter, &mi->mi_filter0 ); \ + mi->mi_filter = db##_filter; \ + mi->mi_attrkeys = db##_keys; \ + BER_BVZERO(&mi->mi_base); \ + } + +/* param structure for search callback */ +#define NSSOV_CBPRIV(db,parms) \ + typedef struct nssov_##db##_cbp { \ + nssov_mapinfo *mi; \ + TFILE *fp; \ + Operation *op; \ + parms \ + } nssov_##db##_cbp + +/* callback for writing search results */ +#define NSSOV_CB(db) \ + static int nssov_##db##_cb(Operation *op, SlapReply *rs) \ + { \ + if ( rs->sr_type == REP_SEARCH ) { \ + nssov_##db##_cbp *cbp = op->o_callback->sc_private; \ + if (write_##db(cbp,rs->sr_entry)) return LDAP_OTHER; \ + } \ + return LDAP_SUCCESS; \ + } \ + +/* macro for generating service handling code */ +#define NSSOV_HANDLE(db,fn,readfn,logcall,action,mkfilter) \ + int nssov_##db##_##fn(nssov_info *ni,TFILE *fp,Operation *op) \ + { \ + /* define common variables */ \ + int32_t tmpint32; \ + nssov_##db##_cbp cbp; \ + slap_callback cb = {0}; \ + SlapReply rs = {REP_RESULT}; \ + cbp.mi = &ni->ni_maps[NM_##db]; \ + cbp.fp = fp; \ + cbp.op = op; \ + /* read request parameters */ \ + readfn; \ + /* log call */ \ + logcall; \ + /* write the response header */ \ + WRITE_INT32(fp,NSLCD_VERSION); \ + WRITE_INT32(fp,action); \ + /* prepare the search filter */ \ + if (mkfilter) \ + { \ + Debug(LDAP_DEBUG_ANY,"nssov_" __STRING(db) "_" __STRING(fn) "(): filter buffer too small",0,0,0); \ + return -1; \ + } \ + cb.sc_private = &cbp; \ + op->o_callback = &cb; \ + cb.sc_response = nssov_##db##_cb; \ + slap_op_time( &op->o_time, &op->o_tincr ); \ + op->o_req_dn = cbp.mi->mi_base; \ + op->o_req_ndn = cbp.mi->mi_base; \ + op->ors_scope = cbp.mi->mi_scope; \ + op->ors_filterstr = filter; \ + op->ors_filter = str2filter_x( op, filter.bv_val ); \ + op->ors_attrs = cbp.mi->mi_attrs; \ + op->ors_tlimit = SLAP_NO_LIMIT; \ + op->ors_slimit = SLAP_NO_LIMIT; \ + /* do the internal search */ \ + op->o_bd->be_search( op, &rs ); \ + filter_free_x( op, op->ors_filter, 1 ); \ + WRITE_INT32(fp,NSLCD_RESULT_END); \ + return 0; \ + } + +#endif /* NSSOV_H */ diff --git a/contrib/slapd-modules/nssov/pam.c b/contrib/slapd-modules/nssov/pam.c new file mode 100644 index 0000000..fc4f1ee --- /dev/null +++ b/contrib/slapd-modules/nssov/pam.c @@ -0,0 +1,864 @@ +/* pam.c - pam processing routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 by Howard Chu, Symas Corp. + * Portions Copyright 2013 by Ted C. Cheng, 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" + +#undef ldap_debug /* silence a warning in ldap-int.h */ +#include "../../../libraries/libldap/ldap-int.h" /* for ldap_ld_free */ + +static int ppolicy_cid; +static AttributeDescription *ad_loginStatus; + +struct paminfo { + struct berval uid; + struct berval dn; + struct berval svc; + struct berval ruser; + struct berval rhost; + struct berval tty; + struct berval pwd; + int authz; + struct berval msg; + int ispwdmgr; +}; + +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; +} + +static int pam_uid2dn(nssov_info *ni, Operation *op, + struct paminfo *pi) +{ + struct berval sdn; + + BER_BVZERO(&pi->dn); + + if (!isvalidusername(&pi->uid)) { + Debug(LDAP_DEBUG_ANY,"nssov_pam_uid2dn(%s): invalid user name\n", + pi->uid.bv_val ? pi->uid.bv_val : "NULL",0,0); + return NSLCD_PAM_USER_UNKNOWN; + } + + 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 ); + } + } + if (BER_BVISEMPTY(&pi->dn)) { + return NSLCD_PAM_USER_UNKNOWN; + } + return 0; +} + +int pam_do_bind(nssov_info *ni,TFILE *fp,Operation *op, + struct paminfo *pi) +{ + int rc; + slap_callback cb = {0}; + SlapReply rs = {REP_RESULT}; + + pi->msg.bv_val = pi->pwd.bv_val; + pi->msg.bv_len = 0; + pi->authz = NSLCD_PAM_SUCCESS; + + if (!pi->ispwdmgr) { + + rc = pam_uid2dn(ni, op, pi); + if (rc) goto finish; + + if (BER_BVISEMPTY(&pi->pwd)) { + rc = NSLCD_PAM_PERM_DENIED; + 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: + Debug(LDAP_DEBUG_ANY,"pam_do_bind (%s): rc (%d)\n", + pi->dn.bv_val ? pi->dn.bv_val : "NULL", rc, 0); + return rc; +} + +int pam_authc(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid) +{ + int32_t tmpint32; + int rc; + char uidc[32]; + char svcc[256]; + char ruserc[32]; + char rhostc[256]; + char ttyc[256]; + char pwdc[256]; + struct paminfo pi; + + + READ_STRING(fp,uidc); + pi.uid.bv_val = uidc; + pi.uid.bv_len = tmpint32; + READ_STRING(fp,svcc); + pi.svc.bv_val = svcc; + pi.svc.bv_len = tmpint32; + READ_STRING(fp,ruserc); + pi.ruser.bv_val = ruserc; + pi.ruser.bv_len = tmpint32; + READ_STRING(fp,rhostc); + pi.rhost.bv_val = rhostc; + pi.rhost.bv_len = tmpint32; + READ_STRING(fp,ttyc); + pi.tty.bv_val = ttyc; + pi.tty.bv_len = tmpint32; + READ_STRING(fp,pwdc); + pi.pwd.bv_val = pwdc; + pi.pwd.bv_len = tmpint32; + + Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s)\n", + pi.uid.bv_val ? pi.uid.bv_val : "NULL",0,0); + + BER_BVZERO(&pi.msg); + pi.ispwdmgr = 0; + + /* if service is "passwd" and "nssov-pam-password-prohibit-message */ + /* is set, deny the auth request */ + if (!strcmp(svcc, "passwd") && + !BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) { + Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(): %s (%s)\n", + "password_prohibit_message for passwd", + ni->ni_pam_password_prohibit_message.bv_val,0); + ber_str2bv(ni->ni_pam_password_prohibit_message.bv_val, 0, 0, &pi.msg); + pi.authz = NSLCD_PAM_PERM_DENIED; + rc = NSLCD_PAM_PERM_DENIED; + goto finish; + } + + /* if username is null, pwdmgr password preliminary check */ + if (BER_BVISEMPTY(&pi.uid)) { + if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_dn)) { + /* pwdmgr dn not configured */ + Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n", + "pwdmgr dn not configured", 0, 0); + ber_str2bv("pwdmgr dn not configured", 0, 0, &pi.msg); + pi.authz = NSLCD_PAM_PERM_DENIED; + rc = NSLCD_PAM_PERM_DENIED; + goto finish; + } else if (calleruid != 0) { + Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n", + "caller is not root", 0, 0); + ber_str2bv("only root may do that", 0, 0, &pi.msg); + pi.authz = NSLCD_PAM_PERM_DENIED; + rc = NSLCD_PAM_PERM_DENIED; + goto finish; + } else { + /* use pwdmgr dn */ + ber_str2bv(ni->ni_pam_pwdmgr_dn.bv_val, 0, 0, &pi.dn); + } + + /* use pwdmgr pwd if configured */ + if (BER_BVISEMPTY(&pi.pwd)) { + if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_pwd)) { + Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n", + "no pwdmgr pwd", 0, 0); + ber_str2bv("pwdmgr pwd not configured", 0, 0, &pi.msg); + pi.authz = NSLCD_PAM_PERM_DENIED; + rc = NSLCD_PAM_PERM_DENIED; + goto finish; + } + /* use configured pwdmgr pwd */ + memset((void *) pwdc, 0, 256); + strncpy(pi.pwd.bv_val, ni->ni_pam_pwdmgr_pwd.bv_val, + ni->ni_pam_pwdmgr_pwd.bv_len); + pi.pwd.bv_len = ni->ni_pam_pwdmgr_pwd.bv_len; + } + pi.ispwdmgr = 1; + } + + + rc = pam_do_bind(ni, fp, op, &pi); + +finish: + Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s): rc (%d)\n", + pi.dn.bv_val ? pi.dn.bv_val : "NULL",rc,0); + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC); + WRITE_INT32(fp,NSLCD_RESULT_BEGIN); + WRITE_INT32(fp,rc); + WRITE_BERVAL(fp,&pi.uid); + WRITE_INT32(fp,pi.authz); /* authz */ + WRITE_BERVAL(fp,&pi.msg); /* authzmsg */ + WRITE_INT32(fp,NSLCD_RESULT_END); + 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"); + +static int pam_compare_cb(Operation *op, SlapReply *rs) +{ + if (rs->sr_err == LDAP_COMPARE_TRUE) + op->o_callback->sc_private = (void *)1; + return LDAP_SUCCESS; +} + +int pam_authz(nssov_info *ni,TFILE *fp,Operation *op) +{ + struct berval authzmsg = BER_BVNULL; + int32_t tmpint32; + char uidc[32]; + char svcc[256]; + char ruserc[32]; + char rhostc[256]; + char ttyc[256]; + int rc; + struct paminfo pi; + Entry *e = NULL; + Attribute *a; + slap_callback cb = {0}; + + READ_STRING(fp,uidc); + pi.uid.bv_val = uidc; + pi.uid.bv_len = tmpint32; + READ_STRING(fp,svcc); + pi.svc.bv_val = svcc; + pi.svc.bv_len = tmpint32; + READ_STRING(fp,ruserc); + pi.ruser.bv_val = ruserc; + pi.ruser.bv_len = tmpint32; + READ_STRING(fp,rhostc); + pi.rhost.bv_val = rhostc; + pi.rhost.bv_len = tmpint32; + READ_STRING(fp,ttyc); + pi.tty.bv_val = ttyc; + pi.tty.bv_len = tmpint32; + + rc = pam_uid2dn(ni, op, &pi); + if (rc) goto finish; + + Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(%s)\n", + pi.dn.bv_val ? pi.dn.bv_val : "NULL",0,0); + + /* 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; + SlapReply rs = {REP_RESULT}; + op->o_dn = pi.dn; + op->o_ndn = pi.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; + rs_reinit(&rs2, 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 = pam_compare_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 = pi.svc; + op->orc_ava = &ava; + rc = op->o_bd->be_compare( op, &rs ); + if ( cb.sc_private == NULL ) { + 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; + SlapReply rs = {REP_RESULT}; + op->o_callback = &cb; + cb.sc_response = pam_compare_cb; + cb.sc_private = NULL; + 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 = pi.dn; + op->orc_ava = &ava; + rc = op->o_bd->be_compare( op, &rs ); + if ( cb.sc_private == NULL ) { + 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, &pi.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 || attr_valfind( a, + SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | + SLAP_MR_VALUE_OF_SYNTAX, + &global_host_bv, NULL, 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 || attr_valfind( a, + SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | + SLAP_MR_VALUE_OF_SYNTAX, + &pi.svc, NULL, 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_passwd]; + 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) + pi.uid = a->a_vals[0]; + else if (!BER_BVISEMPTY(&ni->ni_pam_template)) + pi.uid = ni->ni_pam_template; + } + rc = NSLCD_PAM_SUCCESS; + +finish: + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHZ); + WRITE_INT32(fp,NSLCD_RESULT_BEGIN); + WRITE_INT32(fp,rc); + WRITE_BERVAL(fp,&authzmsg); + WRITE_INT32(fp,NSLCD_RESULT_END); + if (e) { + be_entry_release_r(op, e); + } + switch (rc) { + case NSLCD_PAM_SUCCESS: + Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(): success\n", 0,0,0); + break; + case NSLCD_PAM_PERM_DENIED: + Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(): %s\n", + authzmsg.bv_val ? authzmsg.bv_val : "NULL",0,0); + break; + default: + Debug(LDAP_DEBUG_TRACE, + "nssov_pam_authz(): permission denied, rc (%d)\n", + rc, 0, 0); + } + return 0; +} + +static int pam_sess(nssov_info *ni,TFILE *fp,Operation *op,int action) +{ + int32_t tmpint32; + char svcc[256]; + char uidc[32]; + char ttyc[32]; + char rhostc[256]; + char ruserc[32]; + char sessionID[64]; + struct paminfo pi; + 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; + int rc = 0; + + READ_STRING(fp,uidc); + pi.uid.bv_val = uidc; + pi.uid.bv_len = tmpint32; + READ_STRING(fp,svcc); + pi.svc.bv_val = svcc; + pi.svc.bv_len = tmpint32; + READ_STRING(fp,ruserc); + pi.ruser.bv_val = ruserc; + pi.ruser.bv_len = tmpint32; + READ_STRING(fp,rhostc); + pi.rhost.bv_val = rhostc; + pi.rhost.bv_len = tmpint32; + READ_STRING(fp,ttyc); + pi.tty.bv_val = ttyc; + pi.tty.bv_len = tmpint32; + + if (action==NSLCD_ACTION_PAM_SESS_O) { + slap_op_time( &op->o_time, &op->o_tincr ); + timestamp.bv_len = sizeof(timebuf); + timestamp.bv_val = timebuf; + stamp = op->o_time; + slap_timestamp( &stamp, ×tamp ); + } else { + READ_STRING(fp,sessionID); + timestamp.bv_val = sessionID; + timestamp.bv_len = tmpint32; + } + + rc = pam_uid2dn(ni, op, &pi); + if (rc) goto done; + + Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(%s)\n", + action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', pi.dn.bv_val,0); + + if (!ni->ni_pam_sessions) { + Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(): %s\n", + action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', + "pam session(s) not configured, ignored",0); + rc = -1; + goto done; + } + + { + int i, found=0; + for (i=0; !BER_BVISNULL(&ni->ni_pam_sessions[i]); i++) { + if (ni->ni_pam_sessions[i].bv_len != pi.svc.bv_len) + continue; + if (!strcasecmp(ni->ni_pam_sessions[i].bv_val, pi.svc.bv_val)) { + found = 1; + break; + } + } + if (!found) { + Debug(LDAP_DEBUG_TRACE, + "nssov_pam_sess_%c(): service(%s) not configured, ignored\n", + action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', + pi.svc.bv_val,0); + rc = -1; + goto done; + } + } + + bv[0].bv_len = timestamp.bv_len + global_host_bv.bv_len + pi.svc.bv_len + + pi.tty.bv_len + pi.ruser.bv_len + pi.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, pi.svc.bv_val, pi.tty.bv_val, + pi.ruser.bv_val, pi.rhost.bv_val); + + Debug(LDAP_DEBUG_TRACE, "nssov_pam_sess_%c(): loginStatus (%s) \n", + action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', bv[0].bv_val,0); + + 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 = pi.dn; + op->o_req_ndn = pi.dn; + if (op->o_bd->be_modify( op, &rs ) != LDAP_SUCCESS) { + Debug(LDAP_DEBUG_TRACE, + "nssov_pam_sess_%c(): modify op failed\n", + action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', + 0,0); + rc = -1; + } + + if ( mod.sml_next ) { + slap_mods_free( mod.sml_next, 1 ); + } + ber_bvarray_free_x( nbv, op->o_tmpmemctx ); + +done:; + + if (rc == 0) { + Debug(LDAP_DEBUG_TRACE, + "nssov_pam_sess_%c(): success\n", + action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', + 0,0); + } + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,action); + WRITE_INT32(fp,NSLCD_RESULT_BEGIN); + if (action==NSLCD_ACTION_PAM_SESS_O) + WRITE_STRING(fp,timestamp.bv_val); + WRITE_INT32(fp,NSLCD_RESULT_END); + 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,uid_t calleruid) +{ + struct berval npw; + int32_t tmpint32; + char uidc[32]; + char svcc[256]; + char ruserc[32]; + char rhostc[256]; + char ttyc[256]; + int asroot; + char opwc[256]; + char npwc[256]; + struct paminfo pi; + int rc; + + READ_STRING(fp,uidc); + pi.uid.bv_val = uidc; + pi.uid.bv_len = tmpint32; + READ_STRING(fp,svcc); + pi.svc.bv_val = svcc; + pi.svc.bv_len = tmpint32; + READ_STRING(fp,ruserc); + pi.ruser.bv_val = svcc; + pi.ruser.bv_len = tmpint32; + READ_STRING(fp,rhostc); + pi.rhost.bv_val = svcc; + pi.rhost.bv_len = tmpint32; + READ_STRING(fp,ttyc); + pi.tty.bv_val = svcc; + pi.tty.bv_len = tmpint32; + READ_INT32(fp, asroot); + READ_STRING(fp,opwc); + pi.pwd.bv_val = opwc; + pi.pwd.bv_len = tmpint32; + READ_STRING(fp,npwc); + npw.bv_val = npwc; + npw.bv_len = tmpint32; + + rc = pam_uid2dn(ni, op, &pi); + if (rc) goto done; + + Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(%s), %s %s\n", + pi.dn.bv_val ? pi.dn.bv_val : "NULL", + pi.uid.bv_val ? pi.uid.bv_val : "NULL", + asroot ? "as root" : "as user"); + + BER_BVZERO(&pi.msg); + pi.ispwdmgr = 0; + + /* nssov_pam prohibits password mod */ + if (!BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) { + Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(): %s (%s)\n", + "password_prohibit_message", + ni->ni_pam_password_prohibit_message.bv_val,0); + ber_str2bv(ni->ni_pam_password_prohibit_message.bv_val, 0, 0, &pi.msg); + rc = NSLCD_PAM_PERM_DENIED; + goto done; + } + + if (asroot) { + if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_dn)) { + Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), %s\n", + "pwdmgr not configured", 0, 0); + ber_str2bv("pwdmgr not configured", 0, 0, &pi.msg); + rc = NSLCD_PAM_PERM_DENIED; + goto done; + } + if (calleruid != 0) { + Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(): %s\n", + "caller is not root", 0, 0); + ber_str2bv("only root may do that", 0, 0, &pi.msg); + rc = NSLCD_PAM_PERM_DENIED; + goto done; + } + /* root user requesting pwmod */ + pi.ispwdmgr = 1; + } + + if (!pi.ispwdmgr && BER_BVISEMPTY(&pi.pwd)) { + Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), %s\n", + "not pwdmgr and old pwd empty", 0, 0); + ber_str2bv("must provide old password", 0, 0, &pi.msg); + rc = NSLCD_PAM_PERM_DENIED; + goto done; + } + + 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.dn)) + ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_ID, + &pi.dn); + /* supply old pwd whenever it's given */ + 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; + + if (pi.ispwdmgr) { + /* root user changing end-user passwords */ + op->o_dn = ni->ni_pam_pwdmgr_dn; + op->o_ndn = ni->ni_pam_pwdmgr_dn; + } else { + /* end-user self-pwd-mod */ + 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; + +done:; + Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), rc (%d)\n", rc, 0, 0); + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD); + WRITE_INT32(fp,NSLCD_RESULT_BEGIN); + WRITE_INT32(fp,rc); + WRITE_BERVAL(fp,&pi.msg); + return 0; +} + +int nssov_pam_init() +{ + int code = 0; + const char *text; + if (!ad_loginStatus) + code = slap_str2ad("loginStatus", &ad_loginStatus, &text); + + return code; +} diff --git a/contrib/slapd-modules/nssov/passwd.c b/contrib/slapd-modules/nssov/passwd.c new file mode 100644 index 0000000..c834d2c --- /dev/null +++ b/contrib/slapd-modules/nssov/passwd.c @@ -0,0 +1,435 @@ +/* passwd.c - password lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +/* ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY + * DESC 'Abstraction of an account with POSIX attributes' + * MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) + * MAY ( userPassword $ loginShell $ gecos $ description ) ) + */ + +/* the basic search filter for searches */ +static struct berval passwd_filter = BER_BVC("(objectClass=posixAccount)"); + +/* the attributes used in searches */ +static struct berval passwd_keys[] = { + BER_BVC("uid"), + BER_BVC("userPassword"), + BER_BVC("uidNumber"), + BER_BVC("gidNumber"), + BER_BVC("gecos"), + BER_BVC("cn"), + BER_BVC("homeDirectory"), + BER_BVC("loginShell"), + BER_BVC("objectClass"), + BER_BVNULL +}; + +#define UID_KEY 0 +#define PWD_KEY 1 +#define UIDN_KEY 2 +#define GIDN_KEY 3 +#define GEC_KEY 4 +#define CN_KEY 5 +#define DIR_KEY 6 +#define SHL_KEY 7 + +/* default values for attributes */ +static struct berval default_passwd_userPassword = BER_BVC("*"); /* unmatchable */ +static struct berval default_passwd_homeDirectory = BER_BVC(""); +static struct berval default_passwd_loginShell = BER_BVC(""); + +static struct berval shadow_passwd = BER_BVC("x"); + +NSSOV_INIT(passwd) + +/* + Checks to see if the specified name is a valid user name. + + This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name + and 3.276 Portable Filename Character Set): + http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426 + http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276 + + The standard defines user names valid if they contain characters from + the set [A-Za-z0-9._-] where the hyphen should not be used as first + character. As an extension this test allows the dolar '$' sign as the last + character to support Samba special accounts. +*/ +int isvalidusername(struct berval *bv) +{ + int i; + char *name = bv->bv_val; + if ((name==NULL)||(name[0]=='\0')) + return 0; + /* check first character */ + if ( ! ( (name[0]>='A' && name[0] <= 'Z') || + (name[0]>='a' && name[0] <= 'z') || + (name[0]>='0' && name[0] <= '9') || + name[0]=='.' || name[0]=='_' ) ) + return 0; + /* check other characters */ + for (i=1;i<bv->bv_len;i++) + { + if ( name[i]=='$' ) + { + /* if the char is $ we require it to be the last char */ + if (name[i+1]!='\0') + return 0; + } + else if ( ! ( (name[i]>='A' && name[i] <= 'Z') || + (name[i]>='a' && name[i] <= 'z') || + (name[i]>='0' && name[i] <= '9') || + name[i]=='.' || name[i]=='_' || name[i]=='-') ) + return 0; + } + /* no test failed so it must be good */ + return -1; +} + +/* return 1 on success */ +int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid) +{ + nssov_mapinfo *mi = &ni->ni_maps[NM_passwd]; + AttributeDescription *ad = mi->mi_attrs[UID_KEY].an_desc; + Entry *e; + + /* check for empty string */ + if (!dn->bv_len) + return 0; + /* try to look up uid within DN string */ + if (!strncmp(dn->bv_val,ad->ad_cname.bv_val,ad->ad_cname.bv_len) && + dn->bv_val[ad->ad_cname.bv_len] == '=') + { + struct berval bv, rdn; + dnRdn(dn, &rdn); + /* check if it is valid */ + bv.bv_val = dn->bv_val + ad->ad_cname.bv_len + 1; + bv.bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1; + if (!isvalidusername(&bv)) + return 0; + ber_dupbv_x( uid, &bv, op->o_tmpmemctx ); + return 1; + } + /* look up the uid from the entry itself */ + if (be_entry_get_rw( op, dn, NULL, ad, 0, &e) == LDAP_SUCCESS) + { + Attribute *a = attr_find(e->e_attrs, ad); + if (a) { + ber_dupbv_x(uid, &a->a_vals[0], op->o_tmpmemctx); + } + be_entry_release_r(op, e); + if (a) + return 1; + } + return 0; +} + +int nssov_name2dn_cb(Operation *op,SlapReply *rs) +{ + if ( rs->sr_type == REP_SEARCH ) + { + struct berval *bv = op->o_callback->sc_private; + if ( !BER_BVISNULL(bv)) { + op->o_tmpfree( bv->bv_val, op->o_tmpmemctx ); + BER_BVZERO(bv); + return LDAP_ALREADY_EXISTS; + } + ber_dupbv_x(bv, &rs->sr_entry->e_name, op->o_tmpmemctx); + } + return LDAP_SUCCESS; +} + +int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn) +{ + nssov_mapinfo *mi = &ni->ni_maps[NM_passwd]; + char fbuf[1024]; + struct berval filter = {sizeof(fbuf),fbuf}; + slap_callback cb = {0}; + SlapReply rs = {REP_RESULT}; + Operation op2; + int rc; + + /* if it isn't a valid username, just bail out now */ + if (!isvalidusername(uid)) + return 0; + /* we have to look up the entry */ + nssov_filter_byid(mi,UID_KEY,uid,&filter); + BER_BVZERO(dn); + cb.sc_private = dn; + cb.sc_response = nssov_name2dn_cb; + op2 = *op; + op2.o_callback = &cb; + op2.o_req_dn = mi->mi_base; + op2.o_req_ndn = mi->mi_base; + op2.ors_scope = mi->mi_scope; + op2.ors_filterstr = filter; + op2.ors_filter = str2filter_x( op, filter.bv_val ); + op2.ors_attrs = slap_anlist_no_attrs; + op2.ors_tlimit = SLAP_NO_LIMIT; + 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 && !BER_BVISNULL(dn); +} + +/* the maximum number of uidNumber attributes per entry */ +#define MAXUIDS_PER_ENTRY 5 + +NSSOV_CBPRIV(passwd, + char buf[256]; + struct berval name; + struct berval id;); + +static struct berval shadowclass = BER_BVC("shadowAccount"); + +static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry) +{ + int32_t tmpint32; + struct berval tmparr[2], tmpuid[2]; + char *tmp; + struct berval *names; + struct berval *uids; + struct berval passwd = {0}; + gid_t gid; + struct berval gecos; + struct berval homedir; + struct berval shell; + Attribute *a; + int i,j; + int use_shadow = 0; + /* get the usernames for this entry */ + if (BER_BVISNULL(&cbp->name)) + { + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,0); + return 0; + } + names = a->a_vals; + } + else + { + names=tmparr; + names[0]=cbp->name; + BER_BVZERO(&names[1]); + } + /* get the password for this entry */ + a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass); + if ( a ) { + for ( i=0; i<a->a_numvals; i++) { + if ( bvmatch( &shadowclass, &a->a_nvals[i] )) { + use_shadow = 1; + break; + } + } + } + if ( use_shadow ) + { + /* if the entry has a shadowAccount entry, point to that instead */ + passwd = shadow_passwd; + } + else + { + a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc); + if (a) + get_userpassword(&a->a_vals[0], &passwd); + if (BER_BVISNULL(&passwd)) + passwd=default_passwd_userPassword; + } + /* get the uids for this entry */ + if (BER_BVISNULL(&cbp->id)) + { + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,0); + return 0; + } + uids = a->a_vals; + } + else + { + uids = tmpuid; + uids[0] = cbp->id; + BER_BVZERO(&uids[1]); + } + /* get the gid for this 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\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\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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0); + return 0; + } + /* get the gecos for this entry (fall back to cn) */ + a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GEC_KEY].an_desc); + if (!a) + 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\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); + return 0; + } + else if (a->a_numvals > 1) + { + 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); + } + gecos=a->a_vals[0]; + /* get the home directory for this 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0); + homedir=default_passwd_homeDirectory; + } + else + { + if (a->a_numvals > 1) + { + 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 (homedir.bv_val[0]=='\0') + homedir=default_passwd_homeDirectory; + } + /* get the shell for this entry */ + a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc); + if (!a) + { + shell=default_passwd_loginShell; + } + else + { + if (a->a_numvals > 1) + { + 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 (shell.bv_val[0]=='\0') + shell=default_passwd_loginShell; + } + /* write the entries */ + for (i=0;!BER_BVISNULL(&names[i]);i++) + { + if (!isvalidusername(&names[i])) + { + 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 + { + for (j=0;!BER_BVISNULL(&uids[j]);j++) + { + char *tmp; + 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\"\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val, + names[i].bv_val); + continue; + } + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + WRITE_BERVAL(cbp->fp,&names[i]); + WRITE_BERVAL(cbp->fp,&passwd); + WRITE_INT32(cbp->fp,uid); + WRITE_INT32(cbp->fp,gid); + WRITE_BERVAL(cbp->fp,&gecos); + WRITE_BERVAL(cbp->fp,&homedir); + WRITE_BERVAL(cbp->fp,&shell); + } + } + } + return 0; +} + +NSSOV_CB(passwd) + +NSSOV_HANDLE( + passwd,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_STRING(fp,cbp.buf); + 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\n",cbp.name.bv_val,0,0); + return -1; + } + BER_BVZERO(&cbp.id); , + 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) +) + +NSSOV_HANDLE( + passwd,byuid, + uid_t uid; + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_INT32(fp,uid); + 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)\n",cbp.id.bv_val,0,0);, + NSLCD_ACTION_PASSWD_BYUID, + nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter) +) + +NSSOV_HANDLE( + passwd,all, + struct berval filter; + /* no parameters to read */ + BER_BVZERO(&cbp.name); + BER_BVZERO(&cbp.id);, + Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()\n",0,0,0);, + NSLCD_ACTION_PASSWD_ALL, + (filter=cbp.mi->mi_filter,0) +) diff --git a/contrib/slapd-modules/nssov/protocol.c b/contrib/slapd-modules/nssov/protocol.c new file mode 100644 index 0000000..f9f1173 --- /dev/null +++ b/contrib/slapd-modules/nssov/protocol.c @@ -0,0 +1,156 @@ +/* protocol.c - network protocol lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 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>. + */ +/* + * ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +/* ( nisSchema.2.4 NAME 'ipProtocol' SUP top STRUCTURAL + * DESC 'Abstraction of an IP protocol. Maps a protocol number + * to one or more names. The distinguished value of the cn + * attribute denotes the protocol's canonical name' + * MUST ( cn $ ipProtocolNumber ) + * MAY description ) + */ + +/* the basic search filter for searches */ +static struct berval protocol_filter = BER_BVC("(objectClass=ipProtocol)"); + +/* the attributes used in searches */ +static struct berval protocol_keys[] = { + BER_BVC("cn"), + BER_BVC("ipProtocolNumber"), + BER_BVNULL +}; + +NSSOV_INIT(protocol) + +NSSOV_CBPRIV(protocol, + char buf[256]; + struct berval name; + struct berval numb;); + +static int write_protocol(nssov_protocol_cbp *cbp,Entry *entry) +{ + int32_t tmpint32; + int i,numname,dupname,proto; + struct berval name,*names; + Attribute *a; + char *tmp; + + /* get the most canonical name */ + nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name ); + /* get the other names for the protocol */ + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val, 0 ); + return 0; + } + names = a->a_vals; + numname = a->a_numvals; + /* if the name is not yet found, get the first entry from names */ + if (BER_BVISNULL(&name)) { + name=names[0]; + dupname = 0; + } else { + dupname = -1; + for (i=0; i<numname; i++) { + if ( bvmatch(&name, &a->a_nvals[i])) { + dupname = i; + break; + } + } + } + /* get the protocol number */ + 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\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\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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 ); + return 0; + } + /* write the entry */ + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + WRITE_BERVAL(cbp->fp,&name); + if ( dupname >= 0 ) { + WRITE_INT32(cbp->fp,numname-1); + } else { + WRITE_INT32(cbp->fp,numname); + } + for (i=0;i<numname;i++) { + if (i == dupname) continue; + WRITE_BERVAL(cbp->fp,&names[i]); + } + WRITE_INT32(cbp->fp,proto); + return 0; +} + +NSSOV_CB(protocol) + +NSSOV_HANDLE( + protocol,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + BER_BVZERO(&cbp.numb); + READ_STRING(fp,cbp.buf); + cbp.name.bv_len = tmpint32; + cbp.name.bv_val = cbp.buf;, + 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) +) + +NSSOV_HANDLE( + protocol,bynumber, + int protocol; + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_INT32(fp,protocol); + 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)\n",cbp.numb.bv_val,0,0);, + NSLCD_ACTION_PROTOCOL_BYNUMBER, + nssov_filter_byid(cbp.mi,1,&cbp.numb,&filter) +) + +NSSOV_HANDLE( + protocol,all, + struct berval filter; + /* no parameters to read */, + Debug(LDAP_DEBUG_TRACE,"nssov_protocol_all()\n",0,0,0);, + NSLCD_ACTION_PROTOCOL_ALL, + (filter=cbp.mi->mi_filter,0) +) diff --git a/contrib/slapd-modules/nssov/rpc.c b/contrib/slapd-modules/nssov/rpc.c new file mode 100644 index 0000000..ad9f70e --- /dev/null +++ b/contrib/slapd-modules/nssov/rpc.c @@ -0,0 +1,158 @@ +/* rpc.c - rpc lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +/* ( nisSchema.2.5 NAME 'oncRpc' SUP top STRUCTURAL + * DESC 'Abstraction of an Open Network Computing (ONC) + * [RFC1057] Remote Procedure Call (RPC) binding. + * This class maps an ONC RPC number to a name. + * The distinguished value of the cn attribute denotes + * the RPC service's canonical name' + * MUST ( cn $ oncRpcNumber ) + * MAY description ) + */ + +/* the basic search filter for searches */ +static struct berval rpc_filter = BER_BVC("(objectClass=oncRpc)"); + +/* the attributes to request with searches */ +static struct berval rpc_keys[] = { + BER_BVC("cn"), + BER_BVC("oncRpcNumber"), + BER_BVNULL +}; + +NSSOV_INIT(rpc) + +NSSOV_CBPRIV(rpc, + char buf[256]; + struct berval name; + struct berval numb;); + +/* write a single rpc entry to the stream */ +static int write_rpc(nssov_rpc_cbp *cbp,Entry *entry) +{ + int32_t tmpint32; + int i,numname,dupname,number; + struct berval name,*names; + Attribute *a; + char *tmp; + + /* get the most canonical name */ + nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name ); + /* get the other names for the rpc */ + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val, 0 ); + return 0; + } + names = a->a_vals; + numname = a->a_numvals; + /* if the name is not yet found, get the first entry from names */ + if (BER_BVISNULL(&name)) { + name=names[0]; + dupname = 0; + } else { + dupname = -1; + for (i=0; i<numname; i++) { + if ( bvmatch(&name, &a->a_nvals[i])) { + dupname = i; + break; + } + } + } + /* get the rpc number */ + 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\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\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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 ); + return 0; + } + /* write the entry */ + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + WRITE_BERVAL(cbp->fp,&name); + if ( dupname >= 0 ) { + WRITE_INT32(cbp->fp,numname-1); + } else { + WRITE_INT32(cbp->fp,numname); + } + for (i=0;i<numname;i++) { + if (i == dupname) continue; + WRITE_BERVAL(cbp->fp,&names[i]); + } + WRITE_INT32(cbp->fp,number); + return 0; +} + +NSSOV_CB(rpc) + +NSSOV_HANDLE( + rpc,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + BER_BVZERO(&cbp.numb); + READ_STRING(fp,cbp.buf); + cbp.name.bv_len = tmpint32; + cbp.name.bv_val = cbp.buf;, + 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) +) + +NSSOV_HANDLE( + rpc,bynumber, + int number; + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_INT32(fp,number); + 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(%s)\n",cbp.numb.bv_val,0,0);, + NSLCD_ACTION_RPC_BYNUMBER, + nssov_filter_byid(cbp.mi,1,&cbp.numb,&filter) +) + +NSSOV_HANDLE( + rpc,all, + struct berval filter; + /* no parameters to read */, + Debug(LDAP_DEBUG_TRACE,"nssov_rpc_all()\n",0,0,0);, + NSLCD_ACTION_RPC_ALL, + (filter=cbp.mi->mi_filter,0) +) diff --git a/contrib/slapd-modules/nssov/service.c b/contrib/slapd-modules/nssov/service.c new file mode 100644 index 0000000..181aee6 --- /dev/null +++ b/contrib/slapd-modules/nssov/service.c @@ -0,0 +1,250 @@ +/* service.c - service lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +/* ( nisSchema.2.3 NAME 'ipService' SUP top STRUCTURAL + * DESC 'Abstraction an Internet Protocol service. + * Maps an IP port and protocol (such as tcp or udp) + * to one or more names; the distinguished value of + * the cn attribute denotes the service's canonical + * name' + * MUST ( cn $ ipServicePort $ ipServiceProtocol ) + * MAY ( description ) ) + */ + +/* the basic search filter for searches */ +static struct berval service_filter = BER_BVC("(objectClass=ipService)"); + +/* the attributes to request with searches */ +static struct berval service_keys[] = { + BER_BVC("cn"), + BER_BVC("ipServicePort"), + BER_BVC("ipServiceProtocol"), + BER_BVNULL +}; + +static int mkfilter_service_byname(nssov_mapinfo *mi,struct berval *name, + struct berval *protocol,struct berval *buf) +{ + char buf2[1024],buf3[1024]; + struct berval bv2 = {sizeof(buf2),buf2}; + struct berval bv3 = {sizeof(buf3),buf3}; + + /* escape attributes */ + if (nssov_escape(name,&bv2)) + return -1; + if (!BER_BVISNULL(protocol)) { + if (nssov_escape(protocol,&bv3)) + return -1; + if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[0].an_desc->ad_cname.bv_len + + bv3.bv_len + mi->mi_attrs[2].an_desc->ad_cname.bv_len + 9 > buf->bv_len ) + return -1; + buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s)(%s=%s))", + mi->mi_filter.bv_val, + mi->mi_attrs[0].an_desc->ad_cname.bv_val, bv2.bv_val, + mi->mi_attrs[2].an_desc->ad_cname.bv_val, bv3.bv_val ); + } else { + if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[0].an_desc->ad_cname.bv_len + 6 > + buf->bv_len ) + return -1; + buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))", + mi->mi_filter.bv_val, mi->mi_attrs[0].an_desc->ad_cname.bv_val, + bv2.bv_val ); + } + return 0; +} + +static int mkfilter_service_bynumber(nssov_mapinfo *mi,struct berval *numb, + struct berval *protocol,struct berval *buf) +{ + char buf2[1024]; + struct berval bv2 = {sizeof(buf2),buf2}; + + /* escape attribute */ + if (!BER_BVISNULL(protocol)) { + if (nssov_escape(protocol,&bv2)) + return -1; + if (numb->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[1].an_desc->ad_cname.bv_len + + bv2.bv_len + mi->mi_attrs[2].an_desc->ad_cname.bv_len + 9 > buf->bv_len ) + return -1; + buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s)(%s=%s))", + mi->mi_filter.bv_val, + mi->mi_attrs[1].an_desc->ad_cname.bv_val, numb->bv_val, + mi->mi_attrs[2].an_desc->ad_cname.bv_val, bv2.bv_val ); + } else { + if (numb->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[1].an_desc->ad_cname.bv_len + 6 > + buf->bv_len ) + return -1; + buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))", + mi->mi_filter.bv_val, mi->mi_attrs[1].an_desc->ad_cname.bv_val, + numb->bv_val ); + } + return 0; +} + +NSSOV_INIT(service) + +NSSOV_CBPRIV(service, + char nbuf[256]; + char pbuf[256]; + struct berval name; + struct berval prot;); + +static int write_service(nssov_service_cbp *cbp,Entry *entry) +{ + int32_t tmpint32; + struct berval name,*names,*protos; + struct berval tmparr[2]; + Attribute *a; + char *tmp; + int port; + int i,numname,dupname,numprot; + + /* get the most canonical name */ + nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name ); + /* get the other names for the rpc */ + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val, 0 ); + return 0; + } + names = a->a_vals; + numname = a->a_numvals; + /* if the name is not yet found, get the first entry from names */ + if (BER_BVISNULL(&name)) { + name=names[0]; + dupname = 0; + } else { + dupname = -1; + for (i=0; i<numname; i++) { + if ( bvmatch(&name, &a->a_nvals[i])) { + dupname = i; + break; + } + } + } + /* get the service number */ + 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\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\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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val, 0 ); + return 0; + } + /* get protocols */ + if (BER_BVISNULL(&cbp->prot)) + { + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[2].an_desc->ad_cname.bv_val, 0 ); + return 0; + } + protos = a->a_vals; + numprot = a->a_numvals; + } + else + { + protos=tmparr; + protos[0]=cbp->prot; + BER_BVZERO(&protos[1]); + numprot = 1; + } + /* write the entries */ + for (i=0;i<numprot;i++) + { + int j; + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + WRITE_BERVAL(cbp->fp,&name); + if ( dupname >= 0 ) { + WRITE_INT32(cbp->fp,numname-1); + } else { + WRITE_INT32(cbp->fp,numname); + } + for (j=0;j<numname;j++) { + if (j == dupname) continue; + WRITE_BERVAL(cbp->fp,&names[j]); + } + WRITE_INT32(cbp->fp,port); + WRITE_BERVAL(cbp->fp,&protos[i]); + } + return 0; +} + +NSSOV_CB(service) + +NSSOV_HANDLE( + service,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_STRING(fp,cbp.nbuf); + cbp.name.bv_len = tmpint32; + cbp.name.bv_val = cbp.nbuf; + READ_STRING(fp,cbp.pbuf); + cbp.prot.bv_len = tmpint32; + cbp.prot.bv_val = tmpint32 ? cbp.pbuf : NULL;, + Debug(LDAP_DEBUG_TRACE,"nssov_service_byname(%s,%s)\n",cbp.name.bv_val,cbp.prot.bv_val ? cbp.prot.bv_val : "",0);, + NSLCD_ACTION_SERVICE_BYNAME, + mkfilter_service_byname(cbp.mi,&cbp.name,&cbp.prot,&filter) +) + +NSSOV_HANDLE( + service,bynumber, + int number; + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_INT32(fp,number); + cbp.name.bv_val = cbp.nbuf; + cbp.name.bv_len = snprintf(cbp.nbuf,sizeof(cbp.nbuf),"%d",number); + READ_STRING(fp,cbp.pbuf); + cbp.prot.bv_len = tmpint32; + cbp.prot.bv_val = tmpint32 ? cbp.pbuf : NULL;, + 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) +) + +NSSOV_HANDLE( + service,all, + struct berval filter; + /* no parameters to read */ + BER_BVZERO(&cbp.prot);, + Debug(LDAP_DEBUG_TRACE,"nssov_service_all()\n",0,0,0);, + NSLCD_ACTION_SERVICE_ALL, + (filter=cbp.mi->mi_filter,0) +) diff --git a/contrib/slapd-modules/nssov/shadow.c b/contrib/slapd-modules/nssov/shadow.c new file mode 100644 index 0000000..d0cfe5d --- /dev/null +++ b/contrib/slapd-modules/nssov/shadow.c @@ -0,0 +1,257 @@ +/* shadow.c - shadow account lookup routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2018 The OpenLDAP Foundation. + * Portions Copyright 2008 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>. + */ +/* ACKNOWLEDGEMENTS: + * This code references portions of the nss-ldapd package + * written by Arthur de Jong. The nss-ldapd code was forked + * from the nss-ldap library written by Luke Howard. + */ + +#include "nssov.h" + +/* ( nisSchema.2.1 NAME 'shadowAccount' SUP top AUXILIARY + * DESC 'Additional attributes for shadow passwords' + * MUST uid + * MAY ( userPassword $ shadowLastChange $ shadowMin + * shadowMax $ shadowWarning $ shadowInactive $ + * shadowExpire $ shadowFlag $ description ) ) + */ + +/* the basic search filter for searches */ +static struct berval shadow_filter = BER_BVC("(objectClass=shadowAccount)"); + +/* the attributes to request with searches */ +static struct berval shadow_keys[] = { + BER_BVC("uid"), + BER_BVC("userPassword"), + BER_BVC("shadowLastChange"), + BER_BVC("shadowMin"), + BER_BVC("shadowMax"), + BER_BVC("shadowWarning"), + BER_BVC("shadowInactive"), + BER_BVC("shadowExpire"), + BER_BVC("shadowFlag"), + BER_BVNULL +}; + +#define UID_KEY 0 +#define PWD_KEY 1 +#define CHG_KEY 2 +#define MIN_KEY 3 +#define MAX_KEY 4 +#define WRN_KEY 5 +#define INA_KEY 6 +#define EXP_KEY 7 +#define FLG_KEY 8 + +/* default values for attributes */ +static struct berval default_shadow_userPassword = BER_BVC("*"); /* unmatchable */ +static int default_nums[] = { 0,0, + -1, /* LastChange */ + -1, /* Min */ + -1, /* Max */ + -1, /* Warning */ + -1, /* Inactive */ + -1, /* Expire */ + 0 /* Flag */ +}; + +NSSOV_INIT(shadow) + +static long to_date(struct berval *date,AttributeDescription *attr) +{ + long value; + char *tmp; + /* do some special handling for date values on AD */ + if (strcasecmp(attr->ad_cname.bv_val,"pwdLastSet")==0) + { + char buffer[8]; + size_t l; + /* we expect an AD 64-bit datetime value; + we should do date=date/864000000000-134774 + but that causes problems on 32-bit platforms, + first we devide by 1000000000 by stripping the + last 9 digits from the string and going from there */ + l=date->bv_len-9; + if (l<1 || l>(sizeof(buffer)-1)) + return 0; /* error */ + strncpy(buffer,date->bv_val,l); + buffer[l]='\0'; + value=strtol(buffer,&tmp,0); + if ((buffer[0]=='\0')||(*tmp!='\0')) + { + Debug(LDAP_DEBUG_ANY,"shadow entry contains non-numeric %s value\n", + attr->ad_cname.bv_val,0,0); + return 0; + } + return value/864-134774; + /* note that AD does not have expiry dates but a lastchangeddate + and some value that needs to be added */ + } + 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\n", + attr->ad_cname.bv_val,0,0); + return 0; + } + return value; +} + +#ifndef UF_DONT_EXPIRE_PASSWD +#define UF_DONT_EXPIRE_PASSWD 0x10000 +#endif + +#define GET_OPTIONAL_LONG(var,key) \ + a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[key].an_desc); \ + if ( !a || BER_BVISNULL(&a->a_vals[0])) \ + var = default_nums[key]; \ + else \ + { \ + if (a->a_numvals > 1) \ + { \ + 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\n", \ + entry->e_name.bv_val, cbp->mi->mi_attrs[key].an_desc->ad_cname.bv_val,0); \ + return 0; \ + } \ + } + +#define GET_OPTIONAL_DATE(var,key) \ + a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[key].an_desc); \ + if ( !a || BER_BVISNULL(&a->a_vals[0])) \ + var = default_nums[key]; \ + else \ + { \ + if (a->a_numvals > 1) \ + { \ + 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); \ + } + +NSSOV_CBPRIV(shadow, + char buf[256]; + struct berval name;); + +static int write_shadow(nssov_shadow_cbp *cbp,Entry *entry) +{ + struct berval tmparr[2]; + struct berval *names; + Attribute *a; + char *tmp; + struct berval passwd = {0}; + long lastchangedate; + long mindays; + long maxdays; + long warndays; + long inactdays; + long expiredate; + unsigned long flag; + int i; + int32_t tmpint32; + /* get username */ + if (BER_BVISNULL(&cbp->name)) + { + 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\n", + entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,0); + return 0; + } + names = a->a_vals; + } + else + { + names=tmparr; + names[0]=cbp->name; + BER_BVZERO(&names[1]); + } + /* get password */ + a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc); + if ( a ) + get_userpassword(&a->a_vals[0], &passwd); + if (BER_BVISNULL(&passwd)) + passwd=default_shadow_userPassword; + /* get lastchange date */ + GET_OPTIONAL_DATE(lastchangedate,CHG_KEY); + /* get mindays */ + GET_OPTIONAL_LONG(mindays,MIN_KEY); + /* get maxdays */ + GET_OPTIONAL_LONG(maxdays,MAX_KEY); + /* get warndays */ + GET_OPTIONAL_LONG(warndays,WRN_KEY); + /* get inactdays */ + GET_OPTIONAL_LONG(inactdays,INA_KEY); + /* get expire date */ + GET_OPTIONAL_LONG(expiredate,EXP_KEY); + /* get flag */ + GET_OPTIONAL_LONG(flag,FLG_KEY); + /* if we're using AD handle the flag specially */ + if (strcasecmp(cbp->mi->mi_attrs[CHG_KEY].an_desc->ad_cname.bv_val,"pwdLastSet")==0) + { + if (flag&UF_DONT_EXPIRE_PASSWD) + maxdays=99999; + flag=0; + } + /* write the entries */ + for (i=0;!BER_BVISNULL(&names[i]);i++) + { + WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); + WRITE_BERVAL(cbp->fp,&names[i]); + WRITE_BERVAL(cbp->fp,&passwd); + WRITE_INT32(cbp->fp,lastchangedate); + WRITE_INT32(cbp->fp,mindays); + WRITE_INT32(cbp->fp,maxdays); + WRITE_INT32(cbp->fp,warndays); + WRITE_INT32(cbp->fp,inactdays); + WRITE_INT32(cbp->fp,expiredate); + WRITE_INT32(cbp->fp,flag); + } + return 0; +} + +NSSOV_CB(shadow) + +NSSOV_HANDLE( + shadow,byname, + char fbuf[1024]; + struct berval filter = {sizeof(fbuf)}; + filter.bv_val = fbuf; + READ_STRING(fp,cbp.buf);, + cbp.name.bv_len = tmpint32; + cbp.name.bv_val = cbp.buf; + 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) +) + +NSSOV_HANDLE( + shadow,all, + struct berval filter; + /* no parameters to read */ + BER_BVZERO(&cbp.name);, + 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 index 0000000..2afbc94 --- /dev/null +++ b/contrib/slapd-modules/nssov/slapo-nssov.5 @@ -0,0 +1,316 @@ +.TH SLAPO-NSSOV 5 "RELEASEDATE" "OpenLDAP LDVERSION" +.\" Copyright 1998-2018 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-pam-ldapd. +An extract of the nss-ldapd source is included along with the +nssov source code to allow the overlay to communicate with the +nss-pam-ldapd client stubs. +.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-pam-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 NSLCDPATH). 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-pam-ldapd. You can get it from: +http://arthurdejong.org/nss-pam-ldapd +You will not need the nslcd daemon; this overlay replaces that part. +To disable building of the nslcd daemon in nss-pam-ldapd, add the +--disable-nslcd option to the nss-pam-ldapd configure script. You +should already be familiar with the RFC2307 and RFC2307bis schema +to use this overlay. See the nss-pam-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 + aliases + ethers + group + hosts + netgroup + networks + passwd + protocols + rpc + services + 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-min-uid <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-max-uid <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 +.TP +.B nssov-pam-password-prohibit-message <message> +Diable password change service and return the specified message to +users. +.TP +.B nssov-pam-pwdmgr-dn <dn> +Specify the dn of the password manager. +.TP +.B nssov-pam-pwdmgr-pwd <pwd> +Specify the pwd of the password manager. +.TP +.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. The rootdn of the database is used to perform +the updates of the loginStatus attribute, so a rootdn must already be +configured for this feature to work. 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 +Enhancements by Ted C. Cheng, Symas Corp. |