diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2006-12-21 20:55:55 +0100 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2006-12-21 20:55:55 +0100 |
commit | fbc5ecfb8cf86d753b7c9a3b5b549a8f279666ab (patch) | |
tree | 5d008fb2963ef8a27da784ba851984f64678e6f0 /nslcd | |
parent | 8366a3eb4a9032ca43cae9fccaa536182dcece04 (diff) |
rename server directory to nslcd
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss-ldapd@196 ef36b2f9-881f-0410-afb5-c4e39611909c
Diffstat (limited to 'nslcd')
-rw-r--r-- | nslcd/Makefile.am | 33 | ||||
-rw-r--r-- | nslcd/alias.c | 137 | ||||
-rw-r--r-- | nslcd/common.c | 42 | ||||
-rw-r--r-- | nslcd/common.h | 89 | ||||
-rw-r--r-- | nslcd/dnsconfig.c | 188 | ||||
-rw-r--r-- | nslcd/dnsconfig.h | 30 | ||||
-rw-r--r-- | nslcd/ether.c | 231 | ||||
-rw-r--r-- | nslcd/group.c | 1231 | ||||
-rw-r--r-- | nslcd/host.c | 380 | ||||
-rw-r--r-- | nslcd/ldap-nss.c | 4032 | ||||
-rw-r--r-- | nslcd/ldap-nss.h | 611 | ||||
-rw-r--r-- | nslcd/ldap-schema.c | 453 | ||||
-rw-r--r-- | nslcd/ldap-schema.h | 303 | ||||
-rw-r--r-- | nslcd/log.c | 187 | ||||
-rw-r--r-- | nslcd/log.h | 60 | ||||
-rw-r--r-- | nslcd/netgroup.c | 354 | ||||
-rw-r--r-- | nslcd/network.c | 245 | ||||
-rw-r--r-- | nslcd/nslcd.c | 656 | ||||
-rw-r--r-- | nslcd/pagectrl.c | 224 | ||||
-rw-r--r-- | nslcd/pagectrl.h | 45 | ||||
-rw-r--r-- | nslcd/passwd.c | 273 | ||||
-rw-r--r-- | nslcd/protocol.c | 198 | ||||
-rw-r--r-- | nslcd/resolve.c | 352 | ||||
-rw-r--r-- | nslcd/resolve.h | 118 | ||||
-rw-r--r-- | nslcd/rpc.c | 206 | ||||
-rw-r--r-- | nslcd/service.c | 302 | ||||
-rw-r--r-- | nslcd/shadow.c | 189 | ||||
-rw-r--r-- | nslcd/util.c | 1669 | ||||
-rw-r--r-- | nslcd/util.h | 121 | ||||
-rw-r--r-- | nslcd/xmalloc.c | 59 | ||||
-rw-r--r-- | nslcd/xmalloc.h | 36 |
31 files changed, 13054 insertions, 0 deletions
diff --git a/nslcd/Makefile.am b/nslcd/Makefile.am new file mode 100644 index 0000000..f451623 --- /dev/null +++ b/nslcd/Makefile.am @@ -0,0 +1,33 @@ +# Makefile.am - use automake to generate Makefile.in +# +# Copyright (C) 2006 West Consulting +# Copyright (C) 2006 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, +# MA 02110-1301 USA + +sbin_PROGRAMS = nslcd +AM_CFLAGS = -pthread + +nslcd_SOURCES = nslcd.c ../nslcd.h ../nslcd-common.h \ + log.c log.h \ + xmalloc.c xmalloc.h \ + common.c common.h \ + alias.c ether.c group.c host.c netgroup.c network.c \ + passwd.c protocol.c rpc.c service.c shadow.c \ + dnsconfig.c dnsconfig.h ldap-nss.c ldap-nss.h \ + ldap-schema.c ldap-schema.h pagectrl.c pagectrl.h \ + resolve.c resolve.h util.c util.h +nslcd_LDADD = @nslcd_LIBS@ diff --git a/nslcd/alias.c b/nslcd/alias.c new file mode 100644 index 0000000..4698f1b --- /dev/null +++ b/nslcd/alias.c @@ -0,0 +1,137 @@ +/* + alias.c - alias entry lookup routines + This file was part of the nss_ldap library (as ldap-alias.c) + which has been forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <lber.h> +#include <ldap.h> +#include <errno.h> +#include <aliases.h> +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +static enum nss_status _nss_ldap_parse_alias( + LDAPMessage *e,struct ldap_state *pvt,void *result, + char *buffer,size_t buflen) +{ + + struct aliasent *alias=(struct aliasent *)result; + enum nss_status stat; + + stat=_nss_ldap_getrdnvalue(e,ATM(LM_ALIASES,cn),&alias->alias_name,&buffer,&buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat=_nss_ldap_assign_attrvals(e,AT(rfc822MailMember),NULL,&alias->alias_members,&buffer,&buflen,&alias->alias_members_len); + + return stat; +} + +static int write_alias(LDAPMessage *e,struct ldap_state *pvt,FILE *fp) +{ + int stat; + if ((stat=_nss_ldap_write_rndvalue(fp,e,ATM(LM_ALIASES,cn)))!=NSLCD_RESULT_SUCCESS) + return stat; + if ((stat=_nss_ldap_write_attrvals(fp,e,AT(rfc822MailMember)))!=NSLCD_RESULT_SUCCESS) + return stat; + return NSLCD_RESULT_SUCCESS; +} + + +/* macros for expanding the NSLCD_ALIAS macro */ +#define NSLCD_STRING(field) WRITE_STRING(fp,field) +#define NSLCD_STRINGLIST(field) WRITE_STRINGLIST_NUM(fp,field,result.alias_members_len) +#define ALIAS_NAME result.alias_name +#define ALIAS_RCPTS result.alias_members + +int nslcd_alias_byname(FILE *fp) +{ + int32_t tmpint32; + char *name; + struct ldap_args a; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* log call */ + log_log(LOG_DEBUG,"nslcd_alias_byname(%s)",name); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_ALIAS_BYNAME); + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + _nss_ldap_searchbyname(&a,_nss_ldap_filt_getaliasbyname,LM_ALIASES,fp,write_alias); + /* no more need for this */ + free(name); + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_alias_all(FILE *fp) +{ + int32_t tmpint32,tmp2int32; + static struct ent_context *alias_context; + /* these are here for now until we rewrite the LDAP code */ + struct aliasent result; + char buffer[1024]; + int errnop; + int retv; + /* log call */ + log_log(LOG_DEBUG,"nslcd_alias_all()"); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_ALIAS_ALL); + /* initialize context */ + if (_nss_ldap_ent_context_init(&alias_context)==NULL) + return -1; + /* loop over all results */ + while ((retv=nss2nslcd(_nss_ldap_getent(&alias_context,&result,buffer,1024,&errnop,_nss_ldap_filt_getaliasent,LM_ALIASES,_nss_ldap_parse_alias)))==NSLCD_RESULT_SUCCESS) + { + /* write the result */ + WRITE_INT32(fp,retv); + NSLCD_ALIAS; + } + /* write the final result code */ + WRITE_INT32(fp,retv); + WRITE_FLUSH(fp); + /* FIXME: if a previous call returns what happens to the context? */ + _nss_ldap_enter(); + _nss_ldap_ent_context_release(alias_context); + _nss_ldap_leave(); + /* we're done */ + return 0; +} diff --git a/nslcd/common.c b/nslcd/common.c new file mode 100644 index 0000000..33d203d --- /dev/null +++ b/nslcd/common.c @@ -0,0 +1,42 @@ +/* + common.c - common server code routines + This file is part of the nss-ldapd library. + + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include "nslcd.h" +#include "common.h" + +/* translates a nslcd return code (as defined in nslcd.h) to + a nss code (as defined in nss.h) */ +/* FIXME: this is a temporary hack, get rid of it */ +int nss2nslcd(enum nss_status code) +{ + switch (code) + { + case NSS_STATUS_UNAVAIL: return NSLCD_RESULT_UNAVAIL; + case NSS_STATUS_NOTFOUND: return NSLCD_RESULT_NOTFOUND; + case NSS_STATUS_SUCCESS: return NSLCD_RESULT_SUCCESS; +/* case NSS_STATUS_TRYAGAIN: return NSLCD_RS_SMALLBUF; */ + default: return NSLCD_RESULT_UNAVAIL; + } +} diff --git a/nslcd/common.h b/nslcd/common.h new file mode 100644 index 0000000..42f47ea --- /dev/null +++ b/nslcd/common.h @@ -0,0 +1,89 @@ +/* + common.h - common server code routines + This file is part of the nss-ldapd library. + + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#ifndef _SERVER_COMMON_H +#define _SERVER_COMMON_H 1 + +#include "nslcd.h" +#include "nslcd-common.h" + + +/* translates a nss code (as defined in nss.h) to a + nslcd return code (as defined in nslcd.h) */ +/* FIXME: this is a temporary hack, get rid of it */ +#include <nss.h> +int nss2nslcd(enum nss_status code); + + +/* 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) \ + log_log(LOG_WARNING,"error writing to client"); \ + return -1; + +#define ERROR_OUT_READERROR(fp) \ + log_log(LOG_WARNING,"error reading from client"); \ + return -1; + +#define ERROR_OUT_ALLOCERROR(fp) \ + log_log(LOG_ERR,"error allocating memory"); \ + return -1; + + +/* these are the different functions that handle the database + specific actions, see nslcd.h for the action descriptions */ +int nslcd_alias_byname(FILE *fp); +int nslcd_alias_all(FILE *fp); +int nslcd_ether_byname(FILE *fp); +int nslcd_ether_byether(FILE *fp); +int nslcd_ether_all(FILE *fp); +int nslcd_group_byname(FILE *fp); +int nslcd_group_bygid(FILE *fp); +int nslcd_group_bymember(FILE *fp); +int nslcd_group_all(FILE *fp); +int nslcd_host_byname(FILE *fp); +int nslcd_host_byaddr(FILE *fp); +int nslcd_host_all(FILE *fp); +int nslcd_netgroup_byname(FILE *fp); +int nslcd_network_byname(FILE *fp); +int nslcd_network_byaddr(FILE *fp); +int nslcd_network_all(FILE *fp); +int nslcd_passwd_byname(FILE *fp); +int nslcd_passwd_byuid(FILE *fp); +int nslcd_passwd_all(FILE *fp); +int nslcd_protocol_byname(FILE *fp); +int nslcd_protocol_bynumber(FILE *fp); +int nslcd_protocol_all(FILE *fp); +int nslcd_rpc_byname(FILE *fp); +int nslcd_rpc_bynumber(FILE *fp); +int nslcd_rpc_all(FILE *fp); +int nslcd_service_byname(FILE *fp); +int nslcd_service_bynumber(FILE *fp); +int nslcd_service_all(FILE *fp); +int nslcd_shadow_byname(FILE *fp); +int nslcd_shadow_all(FILE *fp); + +#endif /* not _SERVER_COMMON_H */ diff --git a/nslcd/dnsconfig.c b/nslcd/dnsconfig.c new file mode 100644 index 0000000..12b0997 --- /dev/null +++ b/nslcd/dnsconfig.c @@ -0,0 +1,188 @@ +/* + dnsconfig.c - lookup code for DNS SRV records + This file was part of the nss_ldap library which has been + forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +/* + * Support DNS SRV records. I look up the SRV record for + * _ldap._tcp.gnu.org. + * and build the DN DC=gnu,DC=org. + * Thanks to Assar & co for resolve.[ch]. + */ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/param.h> +#include <netdb.h> +#include <syslog.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include <string.h> +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "resolve.h" +#include "dnsconfig.h" + +#define DC_ATTR "DC" +#define DC_ATTR_AVA DC_ATTR"=" +#define DC_ATTR_AVA_LEN (sizeof(DC_ATTR_AVA) - 1) + +/* map gnu.org into DC=gnu,DC=org */ +static enum nss_status +_nss_ldap_getdnsdn (char *src_domain, + char **rval, char **buffer, size_t * buflen) +{ + char *p; + int len = 0; +#ifdef HAVE_STRTOK_R + char *st = NULL; +#endif + char *bptr; + char *domain, *domain_copy; + + /* we need to take a copy of domain, because strtok() modifies + * it in place. Bad. + */ + domain_copy = strdup (src_domain); + if (domain_copy == NULL) + { + return NSS_STATUS_TRYAGAIN; + } + + domain = domain_copy; + + bptr = *rval = *buffer; + **rval = '\0'; + +#ifndef HAVE_STRTOK_R + while ((p = strtok (domain, "."))) +#else + while ((p = strtok_r (domain, ".", &st))) +#endif + { + len = strlen (p); + + if (*buflen < (size_t) (len + DC_ATTR_AVA_LEN + 1 /* D C = [,|\0] */ )) + { + free (domain_copy); + return NSS_STATUS_TRYAGAIN; + } + + if (domain == NULL) + { + strcpy (bptr, ","); + bptr++; + } + else + { + domain = NULL; + } + + strcpy (bptr, DC_ATTR_AVA); + bptr += DC_ATTR_AVA_LEN; + + strcpy (bptr, p); + bptr += len; /* don't include comma */ + *buffer += len + DC_ATTR_AVA_LEN + 1; + *buflen -= len + DC_ATTR_AVA_LEN + 1; + } + + if (bptr != NULL) + { + (*rval)[bptr - *rval] = '\0'; + } + + free (domain_copy); + + return NSS_STATUS_SUCCESS; +} + +enum nss_status +_nss_ldap_mergeconfigfromdns (struct ldap_config * result, + char **buffer, size_t *buflen) +{ + enum nss_status stat = NSS_STATUS_SUCCESS; + struct dns_reply *r; + struct resource_record *rr; + char domain[MAXHOSTNAMELEN + 1]; + char *pDomain; + char uribuf[NSS_BUFSIZ]; + + if ((_res.options & RES_INIT) == 0 && res_init () == -1) + { + return NSS_STATUS_UNAVAIL; + } + + if (result->ldc_srv_domain != NULL) + pDomain = result->ldc_srv_domain; + else + { + snprintf (domain, sizeof (domain), "_ldap._tcp.%s.", _res.defdname); + pDomain = domain; + } + + r = dns_lookup (pDomain, "srv"); + if (r == NULL) + { + return NSS_STATUS_NOTFOUND; + } + + /* XXX sort by priority */ + for (rr = r->head; rr != NULL; rr = rr->next) + { + if (rr->type == T_SRV) + { + snprintf (uribuf, sizeof(uribuf), "ldap%s:%s:%d", + (rr->u.srv->port == LDAPS_PORT) ? "s" : "", + rr->u.srv->target, + rr->u.srv->port); + + stat = _nss_ldap_add_uri (result, uribuf, buffer, buflen); + if (stat != NSS_STATUS_SUCCESS) + { + break; + } + } + } + + dns_free_data (r); + stat = NSS_STATUS_SUCCESS; + + if (result->ldc_base == NULL) + { + stat = _nss_ldap_getdnsdn (_res.defdname, &result->ldc_base, + buffer, buflen); + } + + return stat; +} + diff --git a/nslcd/dnsconfig.h b/nslcd/dnsconfig.h new file mode 100644 index 0000000..479a717 --- /dev/null +++ b/nslcd/dnsconfig.h @@ -0,0 +1,30 @@ +/* + dnsconfig.c - lookup code for DNS SRV records + This file was part of the nss_ldap library which has been + forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#ifndef _LDAP_NSS_LDAP_DNSCONFIG_H +#define _LDAP_NSS_LDAP_DNSCONFIG_H + +enum nss_status _nss_ldap_mergeconfigfromdns (struct ldap_config * result, + char **buffer, size_t *buflen); + +#endif /* _LDAP_NSS_LDAP_DNSCONFIG_H */ diff --git a/nslcd/ether.c b/nslcd/ether.c new file mode 100644 index 0000000..ab7b870 --- /dev/null +++ b/nslcd/ether.c @@ -0,0 +1,231 @@ +/* + ether.c - ethernet address entry lookup routines + This file was part of the nss_ldap library (as ldap-ethers.c) + which has been forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif +#ifdef HAVE_NET_ROUTE_H +#include <net/route.h> +#endif +#ifdef HAVE_NETINET_IF_ETHER_H +#include <netinet/if_ether.h> +#endif +#ifdef HAVE_NETINET_ETHER_H +#include <netinet/ether.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +#ifndef HAVE_STRUCT_ETHER_ADDR +struct ether_addr { + u_int8_t ether_addr_octet[6]; +}; +#endif + +struct ether +{ + char *e_name; + struct ether_addr e_addr; +}; + +#ifdef NEW +static int write_ether(LDAPMessage *e,struct ldap_state *pvt,FILE *fp) +{ + int stat; + char buffer[1024]; + /* write NSLCD_STRING(ETHER_NAME) */ + stat=_nss_ldap_write_attrval(fp,e,ATM(LM_ETHERS,cn)); + if (stat!=NSLCD_RESULT_SUCCESS) + return stat; + /* write NSLCD_TYPE(ETHER_ADDR,u_int8_t[6]) */ + stat=_nss_ldap_write_attrval_ether(fp,e,AT(macAddress)); + + stat = _nss_ldap_assign_attrval (e, AT (macAddress), &saddr, + &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS || ((addr = ether_aton (saddr)) == NULL)) + return NSS_STATUS_NOTFOUND; + memcpy (ðer->e_addr, addr, sizeof (*addr)); + return NSLCD_RESULT_SUCCESS; +} +#endif /* NEW */ + +static enum nss_status +_nss_ldap_parse_ether (LDAPMessage * e, + struct ldap_state * pvt, + void *result, char *buffer, size_t buflen) +{ + struct ether *ether = (struct ether *) result; + char *saddr; + enum nss_status stat; + struct ether_addr *addr; + + stat = _nss_ldap_assign_attrval (e, ATM (LM_ETHERS, cn), + ðer->e_name, &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = _nss_ldap_assign_attrval (e, AT (macAddress), &saddr, + &buffer, &buflen); + + if (stat != NSS_STATUS_SUCCESS || ((addr = ether_aton (saddr)) == NULL)) + return NSS_STATUS_NOTFOUND; + + memcpy (ðer->e_addr, addr, sizeof (*addr)); + + return NSS_STATUS_SUCCESS; +} + +/* macros for expanding the NSLCD_ETHER macro */ +#define NSLCD_STRING(field) WRITE_STRING(fp,field) +#define NSLCD_TYPE(field,type) WRITE_TYPE(fp,field,type) +#define ETHER_NAME result.e_name +#define ETHER_ADDR result.e_addr + +int nslcd_ether_byname(FILE *fp) +{ + int32_t tmpint32; + char *name; + struct ldap_args a; + /* these are here for now until we rewrite the LDAP code */ + struct ether result; + char buffer[1024]; + int errnop; + int retv; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* log call */ + log_log(LOG_DEBUG,"nslcd_ether_byname(%s)",name); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_ETHER_BYNAME); + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_gethostton,LM_ETHERS,_nss_ldap_parse_ether)); + /* no more need for this string */ + free(name); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + { + NSLCD_ETHER; + } + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_ether_byether(FILE *fp) +{ + int32_t tmpint32; + struct ether_addr addr; + struct ldap_args a; + /* these are here for now until we rewrite the LDAP code */ + struct ether result; + char buffer[1024]; + int errnop; + int retv; + /* read request parameters */ + READ_TYPE(fp,addr,u_int8_t[6]); + /* log call */ + log_log(LOG_DEBUG,"nslcd_ether_byether(%s)",ether_ntoa(&addr)); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_ETHER_BYETHER); + /* do the LDAP request */ + LA_INIT(a); + /* FIXME: this has a bug when the directory has 01:00:0e:... + and we're looking for 1:0:e:... (leading zeros) */ + LA_STRING(a)=ether_ntoa(&addr); + LA_TYPE(a)=LA_TYPE_STRING; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getntohost,LM_ETHERS,_nss_ldap_parse_ether)); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + { + NSLCD_ETHER; + } + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_ether_all(FILE *fp) +{ + int32_t tmpint32; + static struct ent_context *ether_context; + /* these are here for now until we rewrite the LDAP code */ + struct ether result; + char buffer[1024]; + int errnop; + int retv; + /* log call */ + log_log(LOG_DEBUG,"nslcd_ether_all()"); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_ETHER_ALL); + /* initialize context */ + if (_nss_ldap_ent_context_init(ðer_context)==NULL) + return -1; + /* loop over all results */ + while ((retv=nss2nslcd(_nss_ldap_getent(ðer_context,&result,buffer,1024,&errnop,_nss_ldap_filt_getetherent,LM_ETHERS,_nss_ldap_parse_ether)))==NSLCD_RESULT_SUCCESS) + { + /* write the result */ + WRITE_INT32(fp,retv); + NSLCD_ETHER; + } + /* write the final result code */ + WRITE_INT32(fp,retv); + WRITE_FLUSH(fp); + /* FIXME: if a previous call returns what happens to the context? */ + _nss_ldap_enter(); + _nss_ldap_ent_context_release(ether_context); + _nss_ldap_leave(); + /* we're done */ + return 0; +} diff --git a/nslcd/group.c b/nslcd/group.c new file mode 100644 index 0000000..e537e96 --- /dev/null +++ b/nslcd/group.c @@ -0,0 +1,1231 @@ +/* + group.c - group entry lookup routines + This file was part of the nss_ldap library (as ldap-grp.c) which + has been forked into the nss-ldapd library. + + Copyright (C) 1997-2006 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/param.h> +#include <grp.h> +#include <errno.h> +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +#ifdef HAVE_USERSEC_H +typedef struct ldap_initgroups_args +{ + char *grplist; + size_t listlen; + int depth; + struct name_list *known_groups; + int backlink; +} +ldap_initgroups_args_t; +#else +typedef struct ldap_initgroups_args +{ + gid_t group; + long int *start; + long int *size; + gid_t **groups; + long int limit; + int depth; + struct name_list *known_groups; + int backlink; +} +ldap_initgroups_args_t; +#endif /* HAVE_USERSEC_H */ + +#define LDAP_NSS_MAXGR_DEPTH 16 /* maximum depth of group nesting for getgrent()/initgroups() */ + +#if LDAP_NSS_NGROUPS > 64 +#define LDAP_NSS_BUFLEN_GROUP (NSS_BUFSIZ + (LDAP_NSS_NGROUPS * (sizeof (char *) + LOGNAME_MAX))) +#else +#define LDAP_NSS_BUFLEN_GROUP NSS_BUFSIZ +#endif /* LDAP_NSS_NGROUPS > 64 */ + +#ifndef LOGNAME_MAX +#define LOGNAME_MAX 8 +#endif /* LOGNAME_MAX */ + +#ifndef UID_NOBODY +#define UID_NOBODY (-2) +#endif + +#ifndef GID_NOBODY +#define GID_NOBODY UID_NOBODY +#endif + +static enum nss_status +ng_chase (const char *dn, ldap_initgroups_args_t * lia); + +static enum nss_status +ng_chase_backlink (const char ** membersOf, ldap_initgroups_args_t * lia); + +/* + * Range retrieval logic was reimplemented from example in + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/searching_using_range_retrieval.asp + */ + +static enum nss_status +do_parse_range (const char *attributeType, + const char *attributeDescription, int *start, int *end) +{ + enum nss_status stat = NSS_STATUS_NOTFOUND; + char *attribute; + size_t attributeTypeLength; + size_t attributeDescriptionLength; + char *p; +#ifdef HAVE_STRTOK_R + char *st = NULL; +#endif + + *start = 0; + *end = -1; + + if (strcasecmp (attributeType, attributeDescription) == 0) + { + return NSS_STATUS_SUCCESS; + } + + attributeDescriptionLength = strlen (attributeDescription); + attributeTypeLength = strlen (attributeType); + + if (attributeDescriptionLength < attributeTypeLength) + { + /* could not be a subtype */ + return NSS_STATUS_NOTFOUND; + } + + /* XXX need to copy as strtok() is destructive */ + attribute = strdup (attributeDescription); + if (attribute == NULL) + { + return NSS_STATUS_TRYAGAIN; + } + +#ifndef HAVE_STRTOK_R + for (p = strtok (attribute, ";"); p != NULL; p = strtok (NULL, ";")) +#else + for (p = strtok_r (attribute, ";", &st); + p != NULL; p = strtok_r (NULL, ";", &st)) +#endif /* !HAVE_STRTOK_R */ + { + char *q; + + if (p == attribute) + { + if (strcasecmp (p, attributeType) != 0) + { + free (attribute); + return NSS_STATUS_NOTFOUND; + } + } + else if (strncasecmp (p, "range=", sizeof ("range=") - 1) == 0) + { + p += sizeof ("range=") - 1; + + q = strchr (p, '-'); + if (q == NULL) + { + free (attribute); + return NSS_STATUS_NOTFOUND; + } + + *q++ = '\0'; + + *start = strtoul (p, (char **) NULL, 10); + if (strcmp (q, "*") == 0) + *end = -1; + else + *end = strtoul (q, (char **) NULL, 10); + + stat = NSS_STATUS_SUCCESS; + break; + } + } + + free (attribute); + return stat; +} + +static enum nss_status +do_get_range_values (LDAPMessage * e, + const char *attributeType, + int *start, int *end, char ***pGroupMembers) +{ + enum nss_status stat = NSS_STATUS_NOTFOUND; + BerElement *ber = NULL; + char *attribute; + + *pGroupMembers = NULL; + + for (attribute = _nss_ldap_first_attribute (e, &ber); + attribute != NULL; attribute = _nss_ldap_next_attribute (e, ber)) + { + stat = do_parse_range (attributeType, attribute, start, end); + if (stat == NSS_STATUS_SUCCESS) + { + *pGroupMembers = _nss_ldap_get_values (e, attribute); + if (*pGroupMembers == NULL) + { + stat = NSS_STATUS_NOTFOUND; + } + else if ((*pGroupMembers)[0] == NULL) + { + ldap_value_free (*pGroupMembers); + *pGroupMembers = NULL; + stat = NSS_STATUS_NOTFOUND; + } + } + +#ifdef HAVE_LDAP_MEMFREE + ldap_memfree (attribute); +#endif + + if (stat == NSS_STATUS_SUCCESS) + break; + } + + if (ber != NULL) + ber_free (ber, 0); + + return stat; +} + +/* + * Format an attribute with description as: + * attribute;range=START-END + */ +static enum nss_status +do_construct_range_attribute (const char *attribute, + int start, + int end, + char **buffer, + size_t * buflen, + const char **pAttributeWithRange) +{ + size_t len; + char startbuf[32], endbuf[32]; + + snprintf (startbuf, sizeof (startbuf), "%u", start); + + if (end != -1) + snprintf (endbuf, sizeof (endbuf), "%u", end); + else + snprintf (endbuf, sizeof (endbuf), "*"); + + len = strlen (attribute) + sizeof (";range=") - 1; + len += strlen (startbuf) + 1 /* - */ + strlen (endbuf); + len++; /* \0 */ + + if (*buflen < len) + return NSS_STATUS_TRYAGAIN; + + *pAttributeWithRange = *buffer; + + snprintf (*buffer, len, "%s;range=%s-%s", attribute, startbuf, endbuf); + + *buffer += len; + *buflen -= len; + + return NSS_STATUS_SUCCESS; +} + +/* + * Expand group members, including nested groups + */ +static enum nss_status +do_parse_group_members (LDAPMessage * e, + char ***pGroupMembers, + size_t * pGroupMembersCount, + size_t * pGroupMembersBufferSize, + int *pGroupMembersBufferIsMalloced, + char **buffer, size_t * buflen, + int *depth, + struct name_list **pKnownGroups) /* traversed groups */ +{ + enum nss_status stat = NSS_STATUS_SUCCESS; + char **dnValues = NULL; + char **uidValues = NULL; + char **groupMembers; + size_t groupMembersCount, i; + char **valiter; + /* support for range retrieval */ + const char *uniquemember_attr; + const char *uniquemember_attrs[2]; + LDAPMessage *res = NULL; + int start, end = 0; + char *groupdn = NULL; + + uniquemember_attr = ATM (LM_GROUP, uniqueMember); + + uniquemember_attrs[0] = uniquemember_attr; + uniquemember_attrs[1] = NULL; + + if (*depth > LDAP_NSS_MAXGR_DEPTH) + { + return NSS_STATUS_NOTFOUND; + } + + i = *pGroupMembersCount; /* index of next member */ + groupMembers = *pGroupMembers; + + groupdn = _nss_ldap_get_dn (e); + if (groupdn == NULL) + { + stat = NSS_STATUS_NOTFOUND; + goto out; + } + + if (_nss_ldap_namelist_find (*pKnownGroups, groupdn)) + { + stat = NSS_STATUS_NOTFOUND; + goto out; + } + + /* store group DN for nested group loop detection */ + stat = _nss_ldap_namelist_push (pKnownGroups, groupdn); + if (stat != NSS_STATUS_SUCCESS) + { + goto out; + } + + do + { + if (e == NULL) + { + stat = NSS_STATUS_NOTFOUND; + goto out; + } + + groupMembersCount = 0; /* number of members in this group */ + + (void) do_get_range_values (e, uniquemember_attrs[0], &start, &end, &dnValues); + if (dnValues != NULL) + { + groupMembersCount += ldap_count_values (dnValues); + } + + uidValues = _nss_ldap_get_values (e, ATM (LM_GROUP, memberUid)); + if (uidValues != NULL) + { + groupMembersCount += ldap_count_values (uidValues); + } + + /* + * Check whether we need to increase the group membership buffer. + * As an optimization the buffer is preferentially allocated off + * the stack + */ + if ((i + groupMembersCount) * sizeof (char *) >= + *pGroupMembersBufferSize) + { + *pGroupMembersBufferSize = + (i + groupMembersCount + 1) * sizeof (char *); + *pGroupMembersBufferSize += + (LDAP_NSS_NGROUPS * sizeof (char *)) - 1; + *pGroupMembersBufferSize -= + (*pGroupMembersBufferSize % + (LDAP_NSS_NGROUPS * sizeof (char *))); + + if (*pGroupMembersBufferIsMalloced == 0) + { + groupMembers = *pGroupMembers; + *pGroupMembers = NULL; /* force malloc() */ + } + + *pGroupMembers = + (char **) realloc (*pGroupMembers, *pGroupMembersBufferSize); + if (*pGroupMembers == NULL) + { + *pGroupMembersBufferIsMalloced = 0; /* don't try to free */ + stat = NSS_STATUS_TRYAGAIN; + goto out; + } + + if (*pGroupMembersBufferIsMalloced == 0) + { + memcpy (*pGroupMembers, groupMembers, i * sizeof (char *)); + groupMembers = NULL; /* defensive programming */ + *pGroupMembersBufferIsMalloced = 1; + } + } + + groupMembers = *pGroupMembers; + + /* Parse distinguished name members */ + if (dnValues != NULL) + { + for (valiter = dnValues; *valiter != NULL; valiter++) + { + LDAPMessage *res; + enum nss_status parseStat; + int isNestedGroup = 0; + char *uid; + + uid = strrchr (*valiter, '#'); + if (uid != NULL) + { + *uid = '\0'; + } + + parseStat = _nss_ldap_dn2uid (*valiter, &groupMembers[i], + buffer, buflen, &isNestedGroup, + &res); + if (parseStat == NSS_STATUS_SUCCESS) + { + if (isNestedGroup == 0) + { + /* just a normal user which we have flattened */ + i++; + continue; + } + + (*depth)++; + parseStat = + do_parse_group_members (_nss_ldap_first_entry (res), + &groupMembers, &i, + pGroupMembersBufferSize, + pGroupMembersBufferIsMalloced, + buffer, buflen, depth, + pKnownGroups); + (*depth)--; + + if (parseStat == NSS_STATUS_TRYAGAIN) + { + stat = NSS_STATUS_TRYAGAIN; + goto out; + } + + ldap_msgfree (res); + } + else if (parseStat == NSS_STATUS_TRYAGAIN) + { + stat = NSS_STATUS_TRYAGAIN; + goto out; + } + } + } + + /* Parse RFC 2307 (flat) members */ + if (uidValues != NULL) + { + for (valiter = uidValues; *valiter != NULL; valiter++) + { + size_t len = strlen (*valiter) + 1; + if (*buflen < len) + { + stat = NSS_STATUS_TRYAGAIN; + goto out; + } + groupMembers[i] = *buffer; + *buffer += len; + *buflen -= len; + + memcpy (groupMembers[i++], *valiter, len); + } + } + + /* Get next range for Active Directory compat */ + if (end != -1) + { + stat = do_construct_range_attribute (uniquemember_attr, + end + 1, + -1, + buffer, + buflen, + &uniquemember_attrs[0]); + if (stat == NSS_STATUS_SUCCESS) + { + if (dnValues != NULL) + { + ldap_value_free (dnValues); + dnValues = NULL; + } + if (uidValues != NULL) + { + ldap_value_free (uidValues); + uidValues = NULL; + } + if (res != NULL) + { + ldap_msgfree (res); + res = NULL; + } + + stat = _nss_ldap_read (groupdn, uniquemember_attrs, &res); + if (stat != NSS_STATUS_SUCCESS) + goto out; + + e = _nss_ldap_first_entry (res); + } + } + } + while (end != -1); + +out: + if (dnValues != NULL) + ldap_value_free (dnValues); + if (uidValues != NULL) + ldap_value_free (uidValues); + if (res != NULL) + ldap_msgfree (res); + if (groupdn != NULL) +#ifdef HAVE_LDAP_MEMFREE + ldap_memfree (groupdn); +#else + free (groupdn); +#endif + + *pGroupMembers = groupMembers; + *pGroupMembersCount = i; + + return stat; +} + +/* + * "Fix" group membership list into caller provided buffer, + * and NULL terminate. +*/ +static enum nss_status +do_fix_group_members_buffer (char **mallocedGroupMembers, + size_t groupMembersCount, + char ***pGroupMembers, + char **buffer, size_t * buflen) +{ + size_t len; + + len = (groupMembersCount + 1) * sizeof (char *); + + if (bytesleft (*buffer, *buflen, char *) < len) + { + return NSS_STATUS_TRYAGAIN; + } + + align (*buffer, *buflen, char *); + *pGroupMembers = (char **) *buffer; + *buffer += len; + *buflen -= len; + + memcpy (*pGroupMembers, mallocedGroupMembers, + groupMembersCount * sizeof (char *)); + (*pGroupMembers)[groupMembersCount] = NULL; + + return NSS_STATUS_SUCCESS; +} + +static enum nss_status +_nss_ldap_parse_gr (LDAPMessage * e, + struct ldap_state * pvt, + void *result, char *buffer, size_t buflen) +{ + struct group *gr = (struct group *) result; + char *gid; + enum nss_status stat; + char **groupMembers; + size_t groupMembersCount; + size_t groupMembersBufferSize; + char *groupMembersBuffer[LDAP_NSS_NGROUPS]; + int groupMembersBufferIsMalloced; + int depth; + struct name_list *knownGroups = NULL; + + stat = + _nss_ldap_assign_attrval (e, ATM (LM_GROUP, gidNumber), &gid, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + gr->gr_gid = + (*gid == '\0') ? (unsigned) GID_NOBODY : (gid_t) strtoul (gid, + (char **) NULL, + 10); + + stat = + _nss_ldap_getrdnvalue (e, ATM (LM_GROUP, cn), &gr->gr_name, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = + _nss_ldap_assign_userpassword (e, ATM (LM_GROUP, userPassword), + &gr->gr_passwd, &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + if (_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_RFC2307BIS)) + { + groupMembers = groupMembersBuffer; + groupMembersCount = 0; + groupMembersBufferSize = sizeof (groupMembers); + groupMembersBufferIsMalloced = 0; + depth = 0; + + stat = do_parse_group_members (e, &groupMembers, &groupMembersCount, + &groupMembersBufferSize, + &groupMembersBufferIsMalloced, &buffer, + &buflen, &depth, &knownGroups); + if (stat != NSS_STATUS_SUCCESS) + { + if (groupMembersBufferIsMalloced) + free (groupMembers); + _nss_ldap_namelist_destroy (&knownGroups); + return stat; + } + + stat = do_fix_group_members_buffer (groupMembers, groupMembersCount, + &gr->gr_mem, &buffer, &buflen); + + if (groupMembersBufferIsMalloced) + free (groupMembers); + _nss_ldap_namelist_destroy (&knownGroups); + } + else + { + stat = + _nss_ldap_assign_attrvals (e, ATM (LM_GROUP, memberUid), NULL, + &gr->gr_mem, &buffer, &buflen, NULL); + } + + return stat; +} + +/* + * Add a group ID to a group list, and optionally the group IDs + * of any groups to which this group belongs (RFC2307bis nested + * group expansion is done by do_parse_initgroups_nested()). + */ +static enum nss_status +do_parse_initgroups (LDAPMessage * e, + struct ldap_state * pvt, void *result, + char *buffer, size_t buflen) +{ + char **values; + ssize_t i; + gid_t gid; + ldap_initgroups_args_t *lia = (ldap_initgroups_args_t *) result; + + values = _nss_ldap_get_values (e, ATM (LM_GROUP, gidNumber)); + if (values == NULL) + { + /* invalid group; skip it */ + return NSS_STATUS_NOTFOUND; + } + + if (values[0] == NULL) + { + /* invalid group; skip it */ + ldap_value_free (values); + return NSS_STATUS_NOTFOUND; + } + +#ifdef HAVE_USERSEC_H + i = strlen (values[0]); + lia->grplist = realloc (lia->grplist, lia->listlen + i + 2); + if (lia->grplist == NULL) + { + ldap_value_free (values); + return NSS_STATUS_TRYAGAIN; + } + memcpy (lia->grplist + lia->listlen, values[0], i); + lia->grplist[lia->listlen + i] = ','; + lia->listlen += i + 1; + ldap_value_free (values); +#else + gid = strtoul (values[0], (char **) NULL, 10); + ldap_value_free (values); + + if (gid == LONG_MAX && errno == ERANGE) + { + /* invalid group, skip it */ + return NSS_STATUS_NOTFOUND; + } + + if (gid == lia->group) + { + /* primary group, so skip it */ + return NSS_STATUS_NOTFOUND; + } + + if (lia->limit > 0) + { + if (*(lia->start) >= lia->limit) + { + /* can't fit any more */ + return NSS_STATUS_TRYAGAIN; + } + } + if (*(lia->start) == *(lia->size)) + { + /* Need a bigger buffer */ + *(lia->groups) = (gid_t *) realloc (*(lia->groups), + 2 * *(lia->size) * sizeof (gid_t)); + if (*(lia->groups) == NULL) + { + return NSS_STATUS_TRYAGAIN; + } + *(lia->size) *= 2; + } + + /* weed out duplicates; is this really our responsibility? */ + for (i = 0; i < *(lia->start); i++) + { + if ((*(lia->groups))[i] == gid) + { + return NSS_STATUS_NOTFOUND; + } + } + + /* add to group list */ + (*(lia->groups))[*(lia->start)] = gid; + (*(lia->start)) += 1; +#endif /* HAVE_USERSEC_H */ + + return NSS_STATUS_NOTFOUND; +} + +static enum nss_status +do_parse_initgroups_nested (LDAPMessage * e, + struct ldap_state * pvt, void *result, + char *buffer, size_t buflen) +{ + enum nss_status status; + ldap_initgroups_args_t *lia = (ldap_initgroups_args_t *) result; + char **values; + char *groupdn; + + status = do_parse_initgroups (e, pvt, result, buffer, buflen); + if (status != NSS_STATUS_NOTFOUND) + { + return status; + } + + if (!_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_RFC2307BIS)) + { + return NSS_STATUS_NOTFOUND; + } + + if (lia->backlink != 0) + { + /* + * Now add the GIDs of any groups of which this group is + * a member. + */ + values = _nss_ldap_get_values (e, ATM (LM_GROUP, memberOf)); + if (values != NULL) + { + lia->depth++; + status = ng_chase_backlink ((const char **)values, lia); + lia->depth--; + + ldap_value_free (values); + + return status; + } + } + else + { + /* + * Now add the GIDs of any groups which refer to this group + */ + groupdn = _nss_ldap_get_dn (e); + if (groupdn != NULL) + { + /* Note: there was a problem here with stat in the orriginal code */ + lia->depth++; + status = ng_chase (groupdn, lia); + lia->depth--; +#ifdef HAVE_LDAP_MEMFREE + ldap_memfree (groupdn); +#else + free (groupdn); +#endif + } + } + + return status; +} + +static enum nss_status ng_chase(const char *dn, ldap_initgroups_args_t * lia) +{ + struct ldap_args a; + enum nss_status stat; + struct ent_context *ctx=NULL; + const char *gidnumber_attrs[2]; + int erange; + + if (lia->depth > LDAP_NSS_MAXGR_DEPTH) + return NSS_STATUS_NOTFOUND; + + if (_nss_ldap_namelist_find (lia->known_groups, dn)) + return NSS_STATUS_NOTFOUND; + + gidnumber_attrs[0] = ATM (LM_GROUP, gidNumber); + gidnumber_attrs[1] = NULL; + + LA_INIT (a); + LA_STRING (a) = dn; + LA_TYPE (a) = LA_TYPE_STRING; + + if (_nss_ldap_ent_context_init_locked (&ctx) == NULL) + { + return NSS_STATUS_UNAVAIL; + } + + stat = _nss_ldap_getent_ex (&a, &ctx, lia, NULL, 0, + &erange, _nss_ldap_filt_getgroupsbydn, + LM_GROUP, gidnumber_attrs, + do_parse_initgroups_nested); + + if (stat == NSS_STATUS_SUCCESS) + { + stat = _nss_ldap_namelist_push (&lia->known_groups, dn); + } + + _nss_ldap_ent_context_release (ctx); + free (ctx); + + return stat; +} + +static enum nss_status ng_chase_backlink(const char ** membersOf, ldap_initgroups_args_t * lia) +{ + struct ldap_args a; + enum nss_status stat; + struct ent_context *ctx=NULL; + const char *gidnumber_attrs[3]; + const char **memberP; + const char **filteredMembersOf; /* remove already traversed groups */ + size_t memberCount, i; + int erange; + + if (lia->depth > LDAP_NSS_MAXGR_DEPTH) + return NSS_STATUS_NOTFOUND; + + for (memberCount = 0; membersOf[memberCount] != NULL; memberCount++) + ; + + /* Build a list of membersOf values without any already traversed groups */ + filteredMembersOf = (const char **) malloc(sizeof(char *) * (memberCount + 1)); + if (filteredMembersOf == NULL) + { + return NSS_STATUS_TRYAGAIN; + } + + memberP = filteredMembersOf; + + for (i = 0; i < memberCount; i++) + { + if (_nss_ldap_namelist_find (lia->known_groups, membersOf[i])) + continue; + + *memberP = membersOf[i]; + memberP++; + } + + *memberP = NULL; + + if (filteredMembersOf[0] == NULL) + { + free (filteredMembersOf); + return NSS_STATUS_NOTFOUND; + } + + gidnumber_attrs[0] = ATM (LM_GROUP, gidNumber); + gidnumber_attrs[1] = ATM (LM_GROUP, memberOf); + gidnumber_attrs[2] = NULL; + + LA_INIT (a); + LA_STRING_LIST (a) = filteredMembersOf; + LA_TYPE (a) = LA_TYPE_STRING_LIST_OR; + + if (_nss_ldap_ent_context_init_locked (&ctx) == NULL) + { + free (filteredMembersOf); + return NSS_STATUS_UNAVAIL; + } + + stat = _nss_ldap_getent_ex (&a, &ctx, lia, NULL, 0, + &erange, "(distinguishedName=%s)", + LM_GROUP, gidnumber_attrs, + do_parse_initgroups_nested); + + if (stat == NSS_STATUS_SUCCESS) + { + enum nss_status stat2; + + for (memberP = filteredMembersOf; *memberP != NULL; memberP++) + { + stat2 = _nss_ldap_namelist_push (&lia->known_groups, *memberP); + if (stat2 != NSS_STATUS_SUCCESS) + { + stat = stat2; + break; + } + } + } + + free (filteredMembersOf); + + _nss_ldap_ent_context_release (ctx); + free (ctx); + + return stat; +} + + +static enum nss_status group_bymember(const char *user, long int *start, + long int *size, long int limit, + int *errnop) +{ + ldap_initgroups_args_t lia; + int erange = 0; + char *userdn = NULL; + LDAPMessage *res, *e; + static const char *no_attrs[] = { NULL }; + const char *filter; + struct ldap_args a; + enum nss_status stat; + struct ent_context *ctx=NULL; + const char *gidnumber_attrs[3]; + enum ldap_map_selector map = LM_GROUP; + + LA_INIT (a); + LA_STRING (a) = user; + LA_TYPE (a) = LA_TYPE_STRING; + + debug ("==> group_bymember (user=%s)", LA_STRING (a) ); + + lia.depth = 0; + lia.known_groups = NULL; + + _nss_ldap_enter (); + + /* initialize schema */ + stat = _nss_ldap_init (); + if (stat != NSS_STATUS_SUCCESS) + { + debug ("<== group_bymember (init failed)"); + _nss_ldap_leave (); + return stat; + } + + if (_nss_ldap_test_initgroups_ignoreuser (LA_STRING (a))) + { + debug ("<== group_bymember (user ignored)"); + _nss_ldap_leave (); + return NSS_STATUS_NOTFOUND; + } + + lia.backlink = _nss_ldap_test_config_flag (NSS_LDAP_FLAGS_INITGROUPS_BACKLINK); + + if (lia.backlink != 0) + { + filter = _nss_ldap_filt_getpwnam_groupsbymember; + LA_STRING2 (a) = LA_STRING (a); + LA_TYPE (a) = LA_TYPE_STRING_AND_STRING; + + gidnumber_attrs[0] = ATM (LM_GROUP, gidNumber); + gidnumber_attrs[1] = ATM (LM_GROUP, memberOf); + gidnumber_attrs[2] = NULL; + + map = LM_PASSWD; + } + else + { + if (_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_RFC2307BIS)) + { + /* lookup the user's DN. */ + stat = _nss_ldap_search_s (&a, _nss_ldap_filt_getpwnam, LM_PASSWD, + no_attrs, 1, &res); + if (stat == NSS_STATUS_SUCCESS) + { + e = _nss_ldap_first_entry (res); + if (e != NULL) + { + userdn = _nss_ldap_get_dn (e); + } + ldap_msgfree (res); + } + } + else + { + userdn = NULL; + } + + if (userdn != NULL) + { + LA_STRING2 (a) = userdn; + LA_TYPE (a) = LA_TYPE_STRING_AND_STRING; + filter = _nss_ldap_filt_getgroupsbymemberanddn; + } + else + { + filter = _nss_ldap_filt_getgroupsbymember; + } + + gidnumber_attrs[0] = ATM (LM_GROUP, gidNumber); + gidnumber_attrs[1] = NULL; + } + + if (_nss_ldap_ent_context_init_locked(&ctx)==NULL) + { + debug ("<== group_bymember (ent_context_init failed)"); + _nss_ldap_leave (); + return NSS_STATUS_UNAVAIL; + } + + stat = _nss_ldap_getent_ex (&a, &ctx, (void *) &lia, NULL, 0, + errnop, + filter, + map, + gidnumber_attrs, + do_parse_initgroups_nested); + + if (userdn != NULL) + ldap_memfree (userdn); + + _nss_ldap_namelist_destroy (&lia.known_groups); + _nss_ldap_ent_context_release (ctx); + free (ctx); + _nss_ldap_leave (); + + /* + * We return NSS_STATUS_NOTFOUND to force the parser to be called + * for as many entries (i.e. groups) as exist, for all + * search descriptors. So confusingly this means "success". + */ + if (stat != NSS_STATUS_SUCCESS && stat != NSS_STATUS_NOTFOUND) + { + debug ("<== group_bymember (not found)"); + if (erange) + errno = ERANGE; + return stat; + } + + debug ("<== group_bymember (success)"); + + return NSS_STATUS_SUCCESS; +} + +/* macros for expanding the NSLCD_GROUP macro */ +#define NSLCD_STRING(field) WRITE_STRING(fp,field) +#define NSLCD_TYPE(field,type) WRITE_TYPE(fp,field,type) +#define NSLCD_STRINGLIST(field) WRITE_STRINGLIST_NULLTERM(fp,field) +#define GROUP_NAME result.gr_name +#define GROUP_PASSWD result.gr_passwd +#define GROUP_GID result.gr_gid +#define GROUP_MEMBERS result.gr_mem + +int nslcd_group_byname(FILE *fp) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + char *name; + struct ldap_args a; + /* these are here for now until we rewrite the LDAP code */ + struct group result; + char buffer[1024]; + int errnop; + int retv; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* FIXME: free() this buffer somewhere */ + /* log call */ + log_log(LOG_DEBUG,"nslcd_group_byname(%s)",name); + /* static buffer size check */ + if (1024<LDAP_NSS_BUFLEN_GROUP) + { + log_log(LOG_CRIT,"allocated buffer in nslcd_group_byname() too small"); + exit(1); + } + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getgrnam,LM_GROUP,_nss_ldap_parse_gr)); + /* no more need for this */ + free(name); + /* write the response */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_GROUP_BYNAME); + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + { + NSLCD_GROUP; + } + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_group_bygid(FILE *fp) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + gid_t gid; + struct ldap_args a; + /* these are here for now until we rewrite the LDAP code */ + struct group result; + char buffer[1024]; + int errnop; + int retv; + /* read request parameters */ + READ_TYPE(fp,gid,gid_t); + /* FIXME: free() this buffer somewhere */ + /* log call */ + log_log(LOG_DEBUG,"nslcd_group_bygid(%d)",(int)gid); + /* static buffer size check */ + if (1024<LDAP_NSS_BUFLEN_GROUP) + { + log_log(LOG_CRIT,"allocated buffer in nslcd_group_byname() too small"); + exit(1); + } + /* do the LDAP request */ + LA_INIT(a); + LA_NUMBER(a)=gid; + LA_TYPE(a)=LA_TYPE_NUMBER; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getgrgid,LM_GROUP,_nss_ldap_parse_gr)); + /* write the response */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_GROUP_BYGID); + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + { + NSLCD_GROUP; + } + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_group_bymember(FILE *fp) +{ + int32_t tmpint32; + char *name; + /* these are here for now until we rewrite the LDAP code */ + int errnop; + int retv; + long int start=0,size=1024; + long int i; + gid_t groupsp[1024]; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* FIXME: free() this buffer somewhere */ + /* log call */ + log_log(LOG_DEBUG,"nslcd_group_bymember(%s)",name); + /* do the LDAP request */ + retv=NSLCD_RESULT_NOTFOUND; + /* + retv=nss2nslcd(group_bymember(name,&start,&size,size,&errnop)); + */ + /* Note: we write some garbadge here to ensure protocol error as this + function currently returns incorrect data */ + /* Note: what to do with group ids that are not listed as supplemental + groups but are the user's primary group id? */ + WRITE_INT32(fp,1234); + start=0; + /* TODO: fix this to actually work */ + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_GROUP_BYNAME); + if (retv==NSLCD_RESULT_SUCCESS) + { + /* loop over the returned gids */ + for (i=0;i<start;i++) + { + WRITE_INT32(fp,NSLCD_RESULT_SUCCESS); + /* Note: we will write a fake record here for now. This is because + we want to keep the protocol but currently the only + client application available discards non-gid information */ + WRITE_STRING(fp,""); /* group name */ + WRITE_STRING(fp,"*"); /* group passwd */ + WRITE_TYPE(fp,groupsp[i],gid_t); /* gid */ + WRITE_INT32(fp,1); /* number of members */ + WRITE_STRING(fp,name); /* member=user requested */ + } + WRITE_INT32(fp,NSLCD_RESULT_NOTFOUND); + } + else + { + /* some error occurred */ + WRITE_INT32(fp,retv); + } + WRITE_FLUSH(fp); + /* no more need for this */ + free(name); + /* we're done */ + return 0; +} + +int nslcd_group_all(FILE *fp) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + struct ent_context *gr_context=NULL; + /* these are here for now until we rewrite the LDAP code */ + struct group result; + char buffer[1024]; + int errnop; + int retv; + /* log call */ + log_log(LOG_DEBUG,"nslcd_group_all()"); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_GROUP_ALL); + /* initialize context */ + if (_nss_ldap_ent_context_init(&gr_context)==NULL) + return -1; + /* loop over all results */ + while ((retv=nss2nslcd(_nss_ldap_getent(&gr_context,&result,buffer,1024,&errnop,_nss_ldap_filt_getgrent,LM_GROUP,_nss_ldap_parse_gr)))==NSLCD_RESULT_SUCCESS) + { + /* write the result */ + WRITE_INT32(fp,retv); + NSLCD_GROUP; + } + /* write the final result code */ + WRITE_INT32(fp,retv); + WRITE_FLUSH(fp); + /* FIXME: if a previous call returns what happens to the context? */ + _nss_ldap_enter(); + _nss_ldap_ent_context_release(gr_context); + _nss_ldap_leave(); + /* we're done */ + return 0; +} diff --git a/nslcd/host.c b/nslcd/host.c new file mode 100644 index 0000000..ec04bf2 --- /dev/null +++ b/nslcd/host.c @@ -0,0 +1,380 @@ +/* + host.c - host name lookup routines + This file was part of the nss_ldap library (as ldap-hosts.c) + which has been forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <resolv.h> +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif +#ifdef INET6 +#include <resolv/mapv4v6addr.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +#ifndef MAXALIASES +#define MAXALIASES 35 +#endif + +/* write a single host entry to the stream */ +static int write_hostent(FILE *fp,struct hostent *result) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + int numaddr,i; + /* write the host entry */ + WRITE_STRING(fp,result->h_name); + /* write the alias list */ + WRITE_STRINGLIST_NULLTERM(fp,result->h_aliases); + /* write the number of addresses */ + for (numaddr=0;result->h_addr_list[numaddr]!=NULL;numaddr++) + /*noting*/ ; + WRITE_INT32(fp,numaddr); + /* write the addresses */ + for (i=0;i<numaddr;i++) + { + WRITE_INT32(fp,result->h_addrtype); + WRITE_INT32(fp,result->h_length); + WRITE(fp,result->h_addr_list[i],result->h_length); + } + return 0; +} + +static enum nss_status +_nss_ldap_parse_host (LDAPMessage * e, + struct ldap_state * pvt, + void *result, char *buffer, size_t buflen, + int af) +{ + /* this code needs reviewing. XXX */ + struct hostent *host = (struct hostent *) result; + enum nss_status stat; +#ifdef INET6 + char addressbuf[sizeof ("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") * + MAXALIASES]; +#else + char addressbuf[sizeof ("255.255.255.255") * MAXALIASES]; +#endif + char *p_addressbuf = addressbuf; + char **addresses = NULL; + size_t addresslen = sizeof (addressbuf); + size_t addresscount = 0; + char **host_addresses = NULL; + int i; + + *addressbuf = *buffer = '\0'; + + stat = _nss_ldap_assign_attrval (e, ATM (LM_HOSTS, cn), &host->h_name, + &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = + _nss_ldap_assign_attrvals (e, ATM (LM_HOSTS, cn), host->h_name, + &host->h_aliases, &buffer, &buflen, NULL); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = + _nss_ldap_assign_attrvals (e, AT (ipHostNumber), NULL, &addresses, + &p_addressbuf, &addresslen, &addresscount); + if (stat != NSS_STATUS_SUCCESS) + return stat; + if (addresscount == 0) + return NSS_STATUS_NOTFOUND; + +#ifdef INET6 + if (af == AF_INET6) + { + if (bytesleft (buffer, buflen, char *) < + (size_t) ((addresscount + 1) * IN6ADDRSZ)) + return NSS_STATUS_TRYAGAIN; + } + else + { + if (bytesleft (buffer, buflen, char *) < + (size_t) ((addresscount + 1) * INADDRSZ)) + return NSS_STATUS_TRYAGAIN; + } +#else + if (bytesleft (buffer, buflen, char *) < + (size_t) ((addresscount + 1) * INADDRSZ)) + return NSS_STATUS_TRYAGAIN; +#endif + + align (buffer, buflen, char *); + host_addresses = (char **) buffer; + host->h_addr_list = host_addresses; + host_addresses[addresscount] = NULL; + + buffer += (addresscount + 1) * sizeof (char *); + buflen -= (addresscount + 1) * sizeof (char *); +#ifdef INET6 + host->h_addrtype = 0; + host->h_length = 0; +#else + host->h_addrtype = AF_INET; + host->h_length = INADDRSZ; +#endif + + for (i = 0; i < (int) addresscount; i++) + { +#ifdef INET6 + char *addr = addresses[i]; + char entdata[16]; + /* from glibc NIS parser. Thanks, Uli. */ + + if (af == AF_INET && inet_pton (AF_INET, addr, entdata) > 0) + { + if (_res.options & RES_USE_INET6) + { + map_v4v6_address ((char *) entdata, + (char *) entdata); + host->h_addrtype = AF_INET6; + host->h_length = IN6ADDRSZ; + } + else + { + host->h_addrtype = AF_INET; + host->h_length = INADDRSZ; + } + } + else if (af == AF_INET6 + && inet_pton (AF_INET6, addr, entdata) > 0) + { + host->h_addrtype = AF_INET6; + host->h_length = IN6ADDRSZ; + } + else + /* Illegal address: ignore line. */ + continue; + +#else + unsigned long haddr; + haddr = inet_addr (addresses[i]); +#endif + + if (buflen < (size_t) host->h_length) + return NSS_STATUS_TRYAGAIN; + +#ifdef INET6 + memcpy (buffer, entdata, host->h_length); + *host_addresses = buffer; + buffer += host->h_length; + buflen -= host->h_length; +#else + memcpy (buffer, &haddr, INADDRSZ); + *host_addresses = buffer; + buffer += INADDRSZ; + buflen -= INADDRSZ; +#endif + + host_addresses++; + *host_addresses = NULL; + } + +#ifdef INET6 + /* if host->h_addrtype is not changed, this entry does not + have the right IP address. */ + if (host->h_addrtype == 0) + return NSS_STATUS_NOTFOUND; +#endif + + return NSS_STATUS_SUCCESS; +} + +static enum nss_status +_nss_ldap_parse_hostv4 (LDAPMessage * e, + struct ldap_state * pvt, + void *result, char *buffer, size_t buflen) +{ + return _nss_ldap_parse_host (e, pvt, result, buffer, buflen, + AF_INET); +} + +#ifdef INET6 +static enum nss_status +_nss_ldap_parse_hostv6 (LDAPMessage * e, + struct ldap_state * pvt, + void *result, char *buffer, size_t buflen) +{ + return _nss_ldap_parse_host (e, pvt, result, buffer, buflen, + AF_INET6); +} +#endif + +int nslcd_host_byname(FILE *fp) +{ + int32_t tmpint32; + char *name; + struct ldap_args a; + int retv; + struct hostent result; + char buffer[1024]; + int errnop; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* log call */ + log_log(LOG_DEBUG,"nslcd_host_byname(%s)",name); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_HOST_BYNAME); + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_gethostbyname,LM_HOSTS, +#ifdef INET6 + (af == AF_INET6)?_nss_ldap_parse_hostv6:_nss_ldap_parse_hostv4)); +#else + _nss_ldap_parse_hostv4)); +#endif + /* no more need for this string */ + free(name); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + write_hostent(fp,&result); + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_host_byaddr(FILE *fp) +{ + int32_t tmpint32; + int af; + int len; + char addr[64],name[1024]; + struct ldap_args a; + int retv; + struct hostent result; + char buffer[1024]; + int errnop; + /* read address family */ + READ_INT32(fp,af); + if ((af!=AF_INET)&&(af!=AF_INET6)) + { + log_log(LOG_WARNING,"incorrect address family specified: %d",af); + return -1; + } + /* read address length */ + READ_INT32(fp,len); + if ((len>64)||(len<=0)) + { + log_log(LOG_WARNING,"address length incorrect: %d",len); + return -1; + } + /* read address */ + READ(fp,addr,len); + /* translate the address to a string */ + if (inet_ntop(af,addr,name,1024)==NULL) + { + log_log(LOG_WARNING,"unable to convert address to string"); + return -1; + } + /* log call */ + log_log(LOG_DEBUG,"nslcd_host_byaddr(%s)",name); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_HOST_BYADDR); + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_gethostbyaddr,LM_HOSTS, +#ifdef INET6 + (af == AF_INET6)?_nss_ldap_parse_hostv6:_nss_ldap_parse_hostv4)); +#else + _nss_ldap_parse_hostv4)); +#endif + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + write_hostent(fp,&result); + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_host_all(FILE *fp) +{ + int32_t tmpint32; + static struct ent_context *host_context; + /* these are here for now until we rewrite the LDAP code */ + struct hostent result; + char buffer[1024]; + int errnop; + int retv; + /* log call */ + log_log(LOG_DEBUG,"nslcd_host_all()"); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_HOST_ALL); + /* initialize context */ + if (_nss_ldap_ent_context_init(&host_context)==NULL) + return -1; + /* loop over all results */ + while ((retv=nss2nslcd(_nss_ldap_getent(&host_context,&result,buffer,1024,&errnop,_nss_ldap_filt_gethostent,LM_HOSTS, +#ifdef INET6 + (_res.options&RES_USE_INET6)?_nss_ldap_parse_hostv6:_nss_ldap_parse_hostv4 +#else + _nss_ldap_parse_hostv4 +#endif + )))==NSLCD_RESULT_SUCCESS) + { + /* write the result */ + WRITE_INT32(fp,retv); + write_hostent(fp,&result); + } + /* write the final result code */ + WRITE_INT32(fp,retv); + WRITE_FLUSH(fp); + /* FIXME: if a previous call returns what happens to the context? */ + _nss_ldap_enter(); + _nss_ldap_ent_context_release(host_context); + _nss_ldap_leave(); + /* we're done */ + return 0; +} diff --git a/nslcd/ldap-nss.c b/nslcd/ldap-nss.c new file mode 100644 index 0000000..f442037 --- /dev/null +++ b/nslcd/ldap-nss.c @@ -0,0 +1,4032 @@ +/* + ldap-nss.c - main file for NSS interface + This file was part of the nss_ldap library which has been + forked into the nss-ldapd library. + + Copyright (C) 1997-2006 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <assert.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#include <stdio.h> +#include <syslog.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <errno.h> +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif +#include <netinet/in.h> +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#ifdef HAVE_LDAP_SSL_H +#include <ldap_ssl.h> +#endif +#ifdef HAVE_GSSLDAP_H +#include <gssldap.h> +#endif +#ifdef HAVE_GSSSASL_H +#include <gsssasl.h> +#endif +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif +/* Try to handle systems with both SASL libraries installed */ +#if defined(HAVE_SASL_SASL_H) && defined(HAVE_SASL_AUXPROP_REQUEST) +#include <sasl/sasl.h> +#elif defined(HAVE_SASL_H) +#include <sasl.h> +#endif +#ifdef HAVE_GSSAPI_H +#include <gssapi.h> +#elif defined(HAVE_GSSAPI_GSSAPI_KRB5_H) +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_krb5.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "dnsconfig.h" +#include "pagectrl.h" +#include "common.h" +#include "log.h" + +#if defined(HAVE_THREAD_H) +#ifdef HAVE_PTHREAD_ATFORK +#undef HAVE_PTHREAD_ATFORK +#endif +#endif + +/* how many messages to retrieve results for */ +#ifndef LDAP_MSG_ONE +#define LDAP_MSG_ONE 0x00 +#endif +#ifndef LDAP_MSG_ALL +#define LDAP_MSG_ALL 0x01 +#endif +#ifndef LDAP_MSG_RECEIVED +#define LDAP_MSG_RECEIVED 0x02 +#endif + +#ifdef HAVE_LDAP_LD_FREE +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) +extern int ldap_ld_free (LDAP * ld, int close, LDAPControl **, + LDAPControl **); +#else +extern int ldap_ld_free (LDAP * ld, int close); +#endif /* OPENLDAP 2.x */ +#endif /* HAVE_LDAP_LD_FREE */ + +NSS_LDAP_DEFINE_LOCK (__lock); + +/* + * LS_INIT only used for enumeration contexts + */ +#define LS_INIT(state) do { state.ls_type = LS_TYPE_INDEX; state.ls_retry = 0; state.ls_info.ls_index = -1; } while (0) + +/* + * the configuration is read by the first call to do_open(). + * Pointers to elements of the list are passed around but should not + * be freed. + */ +static char __configbuf[NSS_LDAP_CONFIG_BUFSIZ]; +static struct ldap_config *__config = NULL; + +#ifdef HAVE_SIGACTION +static struct sigaction __stored_handler; +static int __sigaction_retval = -1; +#else +static void (*__sigpipe_handler) (int) = SIG_DFL; +#endif /* HAVE_SIGACTION */ + +/* + * Global LDAP session. + */ +static struct ldap_session __session = { NULL, NULL, 0, LS_UNINITIALIZED }; + +#if defined(HAVE_PTHREAD_ATFORK) || defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H) +static pthread_once_t __once = PTHREAD_ONCE_INIT; +#endif + +#ifdef LBER_OPT_LOG_PRINT_FILE +static FILE *__debugfile; +#endif /* LBER_OPT_LOG_PRINT_FILE */ + +#ifndef HAVE_PTHREAD_ATFORK +/* + * Process ID that opened the session. + */ +static pid_t __pid = -1; +#endif +static uid_t __euid = -1; + +#ifdef HAVE_LDAPSSL_CLIENT_INIT +static int __ssl_initialized = 0; +#endif /* HAVE_LDAPSSL_CLIENT_INIT */ + +#if defined(HAVE_PTHREAD_ATFORK) || defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H) +/* + * Prepare for fork(); lock mutex. + */ +static void do_atfork_prepare (void); + +/* + * Forked in parent, unlock mutex. + */ +static void do_atfork_parent (void); + +/* + * Forked in child; close LDAP socket, unlock mutex. + */ +static void do_atfork_child (void); + +/* + * Install handlers for atfork, called once. + */ +static void do_atfork_setup (void); +#endif + +/* + * Close the global session, sending an unbind. + */ +static void do_close (void); + +/* + * Close the global session without sending an unbind. + */ +static void do_close_no_unbind (void); + +/* + * Disable keepalive on a LDAP connection's socket. + */ +static void do_set_sockopts (void); + +/* + * TLS routines: set global SSL session options. + */ +#if defined(HAVE_LDAP_START_TLS_S) || defined(HAVE_LDAP_START_TLS) || (defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS)) +static int do_ssl_options (struct ldap_config * cfg); +static int do_start_tls (struct ldap_session * session); +#endif + +/* + * Function to be braced by reconnect harness. Used so we + * can apply the reconnect code to both asynchronous and + * synchronous searches. + */ +typedef int (*search_func_t) (const char *, int, const char *, + const char **, int, void *); + +static enum nss_status +do_map_error (int rc) +{ + enum nss_status stat; + + switch (rc) + { + case LDAP_SUCCESS: + case LDAP_SIZELIMIT_EXCEEDED: + case LDAP_TIMELIMIT_EXCEEDED: + stat = NSS_STATUS_SUCCESS; + break; + case LDAP_NO_SUCH_ATTRIBUTE: + case LDAP_UNDEFINED_TYPE: + case LDAP_INAPPROPRIATE_MATCHING: + case LDAP_CONSTRAINT_VIOLATION: + case LDAP_TYPE_OR_VALUE_EXISTS: + case LDAP_INVALID_SYNTAX: + case LDAP_NO_SUCH_OBJECT: + case LDAP_ALIAS_PROBLEM: + case LDAP_INVALID_DN_SYNTAX: + case LDAP_IS_LEAF: + case LDAP_ALIAS_DEREF_PROBLEM: + case LDAP_FILTER_ERROR: + stat = NSS_STATUS_NOTFOUND; + break; + case LDAP_SERVER_DOWN: + case LDAP_TIMEOUT: + case LDAP_UNAVAILABLE: + case LDAP_BUSY: +#ifdef LDAP_CONNECT_ERROR + case LDAP_CONNECT_ERROR: +#endif /* LDAP_CONNECT_ERROR */ + case LDAP_LOCAL_ERROR: + case LDAP_INVALID_CREDENTIALS: + default: + stat = NSS_STATUS_UNAVAIL; + break; + } + return stat; +} + +#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) ||defined (HAVE_SASL_SASL_H)) +static int +do_sasl_interact (LDAP * ld, unsigned flags, void *defaults, void *_interact) +{ + char *authzid = (char *) defaults; + sasl_interact_t *interact = (sasl_interact_t *) _interact; + + while (interact->id != SASL_CB_LIST_END) + { + if (interact->id == SASL_CB_USER) + { + if (authzid != NULL) + { + interact->result = authzid; + interact->len = strlen (authzid); + } + else if (interact->defresult != NULL) + { + interact->result = interact->defresult; + interact->len = strlen (interact->defresult); + } + else + { + interact->result = ""; + interact->len = 0; + } +#if SASL_VERSION_MAJOR < 2 + interact->result = strdup (interact->result); + if (interact->result == NULL) + { + return LDAP_NO_MEMORY; + } +#endif /* SASL_VERSION_MAJOR < 2 */ + } + else + { + return LDAP_PARAM_ERROR; + } + interact++; + } + return LDAP_SUCCESS; +} +#endif + +static int +do_bind (LDAP * ld, int timelimit, const char *dn, const char *pw, + int with_sasl) +{ + int rc; + int msgid; + struct timeval tv; + LDAPMessage *result; + + debug("==> do_bind"); + + /* + * set timelimit in ld for select() call in ldap_pvt_connect() + * function implemented in libldap2's os-ip.c + */ + tv.tv_sec = timelimit; + tv.tv_usec = 0; + +#if (defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H))) || defined(HAVE_LDAP_GSS_BIND) + if (!with_sasl) + { +#endif + msgid = ldap_simple_bind (ld, dn, pw); + + if (msgid < 0) + { + if (ldap_get_option (ld, LDAP_OPT_ERROR_NUMBER, &rc) != + LDAP_SUCCESS) + { + rc = LDAP_UNAVAILABLE; + } + /* Notify if we failed. */ + syslog (LOG_AUTHPRIV | LOG_ERR, "nss_ldap: could not connect to any LDAP server as %s - %s", + dn, ldap_err2string (rc)); + debug ("<== do_bind"); + + return rc; + } + + rc = ldap_result (ld, msgid, 0, &tv, &result); + if (rc > 0) + { + debug ("<== do_bind"); + return ldap_result2error (ld, result, 1); + } + + /* took too long */ + if (rc == 0) + { + ldap_abandon (ld, msgid); + } +#if (defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H))) || defined(HAVE_LDAP_GSS_BIND) + } + else + { +#ifdef HAVE_LDAP_GSS_BIND + return ldap_gss_bind (ld, dn, pw, GSSSASL_NO_SECURITY_LAYER, + LDAP_SASL_GSSAPI); +#else +#ifdef CONFIGURE_KRB5_CCNAME +#ifndef CONFIGURE_KRB5_CCNAME_GSSAPI + char tmpbuf[256]; + static char envbuf[256]; +#endif + char *ccname; + const char *oldccname = NULL; + int retval; +#endif /* CONFIGURE_KRB5_CCNAME */ + + if (__config->ldc_sasl_secprops != NULL) + { + rc = + ldap_set_option (ld, LDAP_OPT_X_SASL_SECPROPS, + (void *) __config->ldc_sasl_secprops); + if (rc != LDAP_SUCCESS) + { + debug ("do_bind: unable to set SASL security properties"); + return rc; + } + } + +#ifdef CONFIGURE_KRB5_CCNAME + /* Set default Kerberos ticket cache for SASL-GSSAPI */ + /* There are probably race conditions here XXX */ + if (__config->ldc_krb5_ccname != NULL) + { + ccname = __config->ldc_krb5_ccname; +#ifdef CONFIGURE_KRB5_CCNAME_ENV + oldccname = getenv ("KRB5CCNAME"); + if (oldccname != NULL) + { + strncpy (tmpbuf, oldccname, sizeof (tmpbuf)); + tmpbuf[sizeof (tmpbuf) - 1] = '\0'; + } + else + { + tmpbuf[0] = '\0'; + } + oldccname = tmpbuf; + snprintf (envbuf, sizeof (envbuf), "KRB5CCNAME=%s", ccname); + putenv (envbuf); +#elif defined(CONFIGURE_KRB5_CCNAME_GSSAPI) + if (gss_krb5_ccache_name (&retval, ccname, &oldccname) != + GSS_S_COMPLETE) + { + debug ("do_bind: unable to set default credential cache"); + return -1; + } +#endif + } +#endif /* CONFIGURE_KRB5_CCNAME */ + + rc = ldap_sasl_interactive_bind_s (ld, dn, "GSSAPI", NULL, NULL, + LDAP_SASL_QUIET, + do_sasl_interact, (void *) pw); + +#ifdef CONFIGURE_KRB5_CCNAME + /* Restore default Kerberos ticket cache. */ + if (oldccname != NULL) + { +#ifdef CONFIGURE_KRB5_CCNAME_ENV + snprintf (envbuf, sizeof (envbuf), "KRB5CCNAME=%s", oldccname); + putenv (envbuf); +#elif defined(CONFIGURE_KRB5_CCNAME_GSSAPI) + if (gss_krb5_ccache_name (&retval, oldccname, NULL) != + GSS_S_COMPLETE) + { + debug ("do_bind: unable to restore default credential cache"); + return -1; + } +#endif + } +#endif /* CONFIGURE_KRB5_CCNAME */ + + return rc; +#endif /* HAVE_LDAP_GSS_BIND */ + } +#endif + + debug ("<== do_bind"); + + return -1; +} + +/* + * Rebind functions. + */ + +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) +#if LDAP_SET_REBIND_PROC_ARGS == 3 +static int +do_rebind (LDAP * ld, LDAP_CONST char *url, ber_tag_t request, + ber_int_t msgid, void *arg) +#else +static int +do_rebind (LDAP * ld, LDAP_CONST char *url, int request, ber_int_t msgid) +#endif +{ + char *who, *cred; + int timelimit; + int with_sasl = 0; + + if (geteuid () == 0 && __session.ls_config->ldc_rootbinddn) + { + who = __session.ls_config->ldc_rootbinddn; +#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H)) + with_sasl = __session.ls_config->ldc_rootusesasl; + if (with_sasl) + { + cred = __session.ls_config->ldc_rootsaslid; + } + else + { +#endif + cred = __session.ls_config->ldc_rootbindpw; +#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H)) + } +#endif + } + else + { + who = __session.ls_config->ldc_binddn; +#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H)) + with_sasl = __session.ls_config->ldc_usesasl; + if (with_sasl) + { + cred = __session.ls_config->ldc_saslid; + } + else + { +#endif + cred = __session.ls_config->ldc_bindpw; +#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H)) + } +#endif + } + + timelimit = __session.ls_config->ldc_bind_timelimit; + +#ifdef HAVE_LDAP_START_TLS_S + if (__session.ls_config->ldc_ssl_on == SSL_START_TLS) + { + int version; + + if (ldap_get_option + (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION, + &version) == LDAP_OPT_SUCCESS) + { + if (version < LDAP_VERSION3) + { + version = LDAP_VERSION3; + ldap_set_option (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION, + &version); + } + } + + if (do_start_tls (&__session) == LDAP_SUCCESS) + { + debug ("TLS startup succeeded"); + } + else + { + debug ("TLS startup failed"); + return NSS_STATUS_UNAVAIL; + } + } +#endif /* HAVE_LDAP_START_TLS_S */ + + return do_bind (ld, timelimit, who, cred, with_sasl); +} +#else +#if LDAP_SET_REBIND_PROC_ARGS == 3 +static int +do_rebind (LDAP * ld, char **whop, char **credp, int *methodp, + int freeit, void *arg) +#elif LDAP_SET_REBIND_PROC_ARGS == 2 +static int +do_rebind (LDAP * ld, char **whop, char **credp, int *methodp, int freeit) +#endif +{ + if (freeit) + { + if (*whop != NULL) + free (*whop); + if (*credp != NULL) + free (*credp); + } + + *whop = *credp = NULL; + if (geteuid () == 0 && __session.ls_config->ldc_rootbinddn) + { + *whop = strdup (__session.ls_config->ldc_rootbinddn); + if (__session.ls_config->ldc_rootbindpw != NULL) + *credp = strdup (__session.ls_config->ldc_rootbindpw); + } + else + { + if (__session.ls_config->ldc_binddn != NULL) + *whop = strdup (__session.ls_config->ldc_binddn); + if (__session.ls_config->ldc_bindpw != NULL) + *credp = strdup (__session.ls_config->ldc_bindpw); + } + + *methodp = LDAP_AUTH_SIMPLE; + + return LDAP_SUCCESS; +} +#endif + +static void +_nss_ldap_block_sigpipe (void) +{ +#ifdef HAVE_SIGACTION + struct sigaction new_handler; + + memset (&new_handler, 0, sizeof (new_handler)); +#if 0 + /* XXX need to test for sa_sigaction, not on all platforms */ + new_handler.sa_sigaction = NULL; +#endif + new_handler.sa_handler = SIG_IGN; + sigemptyset (&new_handler.sa_mask); + new_handler.sa_flags = 0; +#endif /* HAVE_SIGACTION */ + + /* + * Patch for Debian Bug 130006: + * ignore SIGPIPE for all LDAP operations. + * + * The following bug was reintroduced in nss_ldap-213 and is fixed here: + * http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=84344 + * + * See: + * http://www.gnu.org/software/libc/manual/html_node/Signal-and-Sigaction.html + * for more details. + */ +#ifdef HAVE_SIGACTION + __sigaction_retval = sigaction (SIGPIPE, &new_handler, &__stored_handler); +#elif defined(HAVE_SIGSET) + __sigpipe_handler = sigset (SIGPIPE, SIG_IGN); +#else + __sigpipe_handler = signal (SIGPIPE, SIG_IGN); +#endif /* HAVE_SIGSET */ +} + +static void +_nss_ldap_unblock_sigpipe (void) +{ +#ifdef HAVE_SIGACTION + if (__sigaction_retval == 0) + (void) sigaction (SIGPIPE, &__stored_handler, NULL); +#else + if (__sigpipe_handler != SIG_ERR && __sigpipe_handler != SIG_IGN) + { +#ifdef HAVE_SIGSET + (void) sigset (SIGPIPE, __sigpipe_handler); +#else + (void) signal (SIGPIPE, __sigpipe_handler); +#endif /* HAVE_SIGSET */ + } +#endif /* HAVE_SIGACTION */ +} + +#if defined(HAVE_PTHREAD_ATFORK) || defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H) +static void +do_atfork_prepare (void) +{ + debug ("==> do_atfork_prepare"); + NSS_LDAP_LOCK (__lock); + debug ("<== do_atfork_prepare"); +} + +static void +do_atfork_parent (void) +{ + debug ("==> do_atfork_parent"); + NSS_LDAP_UNLOCK (__lock); + debug ("<== do_atfork_parent"); +} + +static void +do_atfork_child (void) +{ + debug ("==> do_atfork_child"); + _nss_ldap_block_sigpipe(); + do_close_no_unbind (); + _nss_ldap_unblock_sigpipe(); + NSS_LDAP_UNLOCK (__lock); + debug ("<== do_atfork_child"); +} + +static void +do_atfork_setup (void) +{ + debug ("==> do_atfork_setup"); + +#ifdef HAVE_PTHREAD_ATFORK + (void) pthread_atfork (do_atfork_prepare, do_atfork_parent, + do_atfork_child); +#elif defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H) + (void) __libc_atfork (do_atfork_prepare, do_atfork_parent, do_atfork_child); +#endif + + debug ("<== do_atfork_setup"); +} +#endif + +/* + * Acquires global lock, blocks SIGPIPE. + */ +void +_nss_ldap_enter (void) +{ + debug ("==> _nss_ldap_enter"); + + NSS_LDAP_LOCK (__lock); + _nss_ldap_block_sigpipe(); + + debug ("<== _nss_ldap_enter"); +} + +/* + * Releases global mutex, releases SIGPIPE. + */ +void +_nss_ldap_leave (void) +{ + debug ("==> _nss_ldap_leave"); + + _nss_ldap_unblock_sigpipe(); + NSS_LDAP_UNLOCK (__lock); + + debug ("<== _nss_ldap_leave"); +} + +static void +do_set_sockopts (void) +{ +/* + * Netscape SSL-enabled LDAP library does not + * return the real socket. + */ +#ifndef HAVE_LDAPSSL_CLIENT_INIT + int sd = -1; + + debug ("==> do_set_sockopts"); + if (ldap_get_option (__session.ls_conn, LDAP_OPT_DESC, &sd) == 0) + { + int off = 0; + socklen_t socknamelen = sizeof (struct sockaddr_storage); + socklen_t peernamelen = sizeof (struct sockaddr_storage); + + (void) setsockopt (sd, SOL_SOCKET, SO_KEEPALIVE, (void *) &off, + sizeof (off)); + (void) fcntl (sd, F_SETFD, FD_CLOEXEC); + /* + * NSS modules shouldn't open file descriptors that the program/utility + * linked against NSS doesn't know about. The LDAP library opens a + * connection to the LDAP server transparently. There's an edge case + * where a daemon might fork a child and, being written well, closes + * all its file descriptors. This will close the socket descriptor + * being used by the LDAP library! Worse, the daemon might open many + * files and sockets, eventually opening a descriptor with the same number + * as that originally used by the LDAP library. The only way to know that + * this isn't "our" socket descriptor is to save the local and remote + * sockaddr_in structures for later comparison. + */ + (void) getsockname (sd, (struct sockaddr *) &__session.ls_sockname, + &socknamelen); + (void) getpeername (sd, (struct sockaddr *) &__session.ls_peername, + &peernamelen); + } + debug ("<== do_set_sockopts"); +#endif /* HAVE_LDAPSSL_CLIENT_INIT */ + + return; +} + +/* + * Closes connection to the LDAP server. + * This assumes that we have exclusive access to __session.ls_conn, + * either by some other function having acquired a lock, or by + * using a thread safe libldap. + */ +static void +do_close (void) +{ +#if defined(DEBUG) || defined(DEBUG_SOCKETS) + int sd = -1; +#endif + + debug ("==> do_close"); + + if (__session.ls_conn != NULL) + { +#if defined(DEBUG) || defined(DEBUG_SOCKETS) +#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_DESC) + ldap_get_option (__session.ls_conn, LDAP_OPT_DESC, &sd); +#else + sd = __session.ls_conn->ld_sb.sb_sd; +#endif /* LDAP_OPT_DESC */ + syslog (LOG_AUTHPRIV | LOG_INFO, "nss_ldap: closing connection %p fd %d", + (void *)__session.ls_conn, sd); +#endif /* DEBUG */ + + ldap_unbind (__session.ls_conn); + __session.ls_conn = NULL; + __session.ls_state = LS_UNINITIALIZED; + } + + debug ("<== do_close"); +} + +static int +do_sockaddr_isequal (struct sockaddr_storage *_s1, + socklen_t _slen1, + struct sockaddr_storage *_s2, + socklen_t _slen2) +{ + int ret; + + if (_s1->ss_family != _s2->ss_family) + return 0; + + if (_slen1 != _slen2) + return 0; + + ret = 0; + + switch (_s1->ss_family) + { + case AF_INET: + { + struct sockaddr_in *s1 = (struct sockaddr_in *) _s1; + struct sockaddr_in *s2 = (struct sockaddr_in *) _s2; + + ret = (s1->sin_port == s2->sin_port && + memcmp (&s1->sin_addr, &s2->sin_addr, sizeof(struct in_addr)) == 0); + break; + } + case AF_UNIX: + { + struct sockaddr_un *s1 = (struct sockaddr_un *) _s1; + struct sockaddr_un *s2 = (struct sockaddr_un *) _s2; + + ret = (memcmp (s1->sun_path, s2->sun_path, + _slen1 - sizeof (_s1->ss_family)) == 0); + break; + } +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 *s1 = (struct sockaddr_in6 *) _s1; + struct sockaddr_in6 *s2 = (struct sockaddr_in6 *) _s2; + + ret = (s1->sin6_port == s2->sin6_port && + memcmp (&s1->sin6_addr, &s2->sin6_addr, sizeof(struct in6_addr)) == 0 && + s1->sin6_scope_id == s2->sin6_scope_id); + break; + } +#endif + default: + ret = (memcmp (_s1, _s2, _slen1) == 0); + break; + } + + return ret; +} + +static int +do_get_our_socket(int *sd) +{ + /* + * Before freeing the LDAP context or closing the socket descriptor, + * we must ensure that it is *our* socket descriptor. See the much + * lengthier description of this at the end of do_open () where the + * values __session.ls_sockname and __session.ls_peername are saved. + * With HAVE_LDAPSSL_CLIENT_INIT this returns 0 if the socket has + * been closed or reopened, and sets *sd to the ldap socket + * descriptor.. Returns 1 in all other cases. + */ + + int isOurSocket = 1; + +#ifndef HAVE_LDAPSSL_CLIENT_INIT + if (ldap_get_option (__session.ls_conn, LDAP_OPT_DESC, sd) == 0) + { + struct sockaddr_storage sockname; + struct sockaddr_storage peername; + socklen_t socknamelen = sizeof (sockname); + socklen_t peernamelen = sizeof (peername); + + if (getsockname (*sd, (struct sockaddr *) &sockname, &socknamelen) != 0 || + getpeername (*sd, (struct sockaddr *) &peername, &peernamelen) != 0) + { + isOurSocket = 0; + } + else + { + isOurSocket = do_sockaddr_isequal (&__session.ls_sockname, + socknamelen, + &sockname, + socknamelen); + if (isOurSocket) + { + isOurSocket = do_sockaddr_isequal (&__session.ls_peername, + peernamelen, + &peername, + peernamelen); + } + } + } +#endif /* HAVE_LDAPSSL_CLIENT_INIT */ + return isOurSocket; +} + +static int +do_dupfd(int oldfd, int newfd) +{ + int d = -1; + int flags; + + flags = fcntl(oldfd, F_GETFD); + + while (1) + { + d = (newfd > -1) ? dup2 (oldfd, newfd) : dup (oldfd); + if (d > -1) + break; + + if (errno == EBADF) + return -1; /* not open */ + + if (errno != EINTR +#ifdef EBUSY + && errno != EBUSY +#endif + ) + return -1; + } + + /* duplicate close-on-exec flag */ + (void) fcntl (d, F_SETFD, flags); + + return d; +} + +static int +do_closefd(int fd) +{ + int rc; + + while ((rc = close(fd)) < 0 && errno == EINTR) + ; + + return rc; +} + +static void +do_drop_connection(int sd, int closeSd) +{ + /* Close the LDAP connection without writing anything to the + underlying socket. The socket will be left open afterwards if + closeSd is 0 */ +#ifndef HAVE_LDAPSSL_CLIENT_INIT + { + int dummyfd = -1, savedfd = -1; + /* Under OpenLDAP 2.x, ldap_set_option (..., LDAP_OPT_DESC, ...) is + a no-op, so to shut down the LDAP connection without writing + anything to the socket, we swap a dummy socket onto that file + descriptor, and then swap the real fd back once the shutdown is + done. */ + savedfd = do_dupfd (sd, -1); + dummyfd = socket (AF_INET, SOCK_STREAM, 0); + if (dummyfd > -1 && dummyfd != sd) + { + do_closefd (sd); + do_dupfd (dummyfd, sd); + do_closefd (dummyfd); + } + +#ifdef HAVE_LDAP_LD_FREE +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) + (void) ldap_ld_free (__session.ls_conn, 0, NULL, NULL); +#else + (void) ldap_ld_free (__session.ls_conn, 0); +#endif /* OPENLDAP 2.x */ +#else + ldap_unbind (__session.ls_conn); +#endif /* HAVE_LDAP_LD_FREE */ + + /* Do we want our original sd back? */ + do_closefd (sd); + if (savedfd > -1) + { + if (closeSd == 0) + do_dupfd (savedfd, sd); + do_closefd (savedfd); + } + } +#else /* No sd available */ + { + int bogusSd = -1; + if (closeSd == 0) + { + sd = -1; /* don't want to really close the socket */ +#ifdef HAVE_LDAP_LD_FREE +#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_DESC) + (void) ldap_set_option (__session.ls_conn, LDAP_OPT_DESC, &sd); +#else + __session.ls_conn->ld_sb.sb_sd = -1; +#endif /* LDAP_OPT_DESC */ +#endif /* HAVE_LDAP_LD_FREE */ + } + +#ifdef HAVE_LDAP_LD_FREE + +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) + (void) ldap_ld_free (__session.ls_conn, 0, NULL, NULL); +#else + (void) ldap_ld_free (__session.ls_conn, 0); +#endif /* OPENLDAP 2.x */ + +#else + +#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_DESC) + (void) ldap_set_option (__session.ls_conn, LDAP_OPT_DESC, &bogusSd); +#else + __session.ls_conn->ld_sb.sb_sd = bogusSd; +#endif /* LDAP_OPT_DESC */ + + /* hope we closed it OK! */ + ldap_unbind (__session.ls_conn); + +#endif /* HAVE_LDAP_LD_FREE */ + + } +#endif /* HAVE_LDAPSSL_CLIENT_INIT */ + __session.ls_conn = NULL; + __session.ls_state = LS_UNINITIALIZED; + + return; +} + +/* + * If we've forked, then we need to open a new session. + * Careful: we have the socket shared with our parent, + * so we don't want to send an unbind to the server. + * However, we want to close the descriptor to avoid + * leaking it, and we also want to release the memory + * used by __session.ls_conn. The only entry point + * we have is ldap_unbind() which does both of these + * things, so we use an internal API, at the expense + * of compatibility. + */ +static void +do_close_no_unbind (void) +{ + int sd = -1; + int closeSd = 1; + + debug ("==> do_close_no_unbind"); + + if (__session.ls_state == LS_UNINITIALIZED) + { + assert (__session.ls_conn == NULL); + debug ("<== do_close_no_unbind (connection was not open)"); + return; + } + + closeSd = do_get_our_socket (&sd); + +#if defined(DEBUG) || defined(DEBUG_SOCKETS) + syslog (LOG_AUTHPRIV | LOG_INFO, "nss_ldap: %sclosing connection (no unbind) %p fd %d", + closeSd ? "" : "not ", (void *)__session.ls_conn, sd); +#endif /* DEBUG */ + + do_drop_connection(sd, closeSd); + + debug ("<== do_close_no_unbind"); + + return; +} + +static enum nss_status +do_init_session (LDAP ** ld, const char *uri, int defport) +{ + int rc; + int ldaps; + char uribuf[NSS_BUFSIZ]; + char *p; + enum nss_status stat; + + ldaps = (strncasecmp (uri, "ldaps://", sizeof ("ldaps://") - 1) == 0); + p = strchr (uri, ':'); + /* we should be looking for the second instance to find the port number */ + if (p != NULL) + { + p = strchr (p, ':'); + } + +#ifdef HAVE_LDAP_INITIALIZE + if (p == NULL && + ((ldaps && defport != LDAPS_PORT) || (!ldaps && defport != LDAP_PORT))) + { + /* No port specified in URI and non-default port specified */ + snprintf (uribuf, sizeof (uribuf), "%s:%d", uri, defport); + uri = uribuf; + } + + rc = ldap_initialize (ld, uri); +#else + if (strncasecmp (uri, "ldap://", sizeof ("ldap://") - 1) != 0) + { + return NSS_STATUS_UNAVAIL; + } + + uri += sizeof ("ldap://") - 1; + p = strchr (uri, ':'); + + if (p != NULL) + { + size_t urilen = (p - uri); + + if (urilen >= sizeof (uribuf)) + { + return NSS_STATUS_UNAVAIL; + } + + memcpy (uribuf, uri, urilen); + uribuf[urilen] = '\0'; + + defport = atoi (p + 1); + uri = uribuf; + } + +#ifdef HAVE_LDAP_INIT + *ld = ldap_init (uri, defport); +#else + *ld = ldap_open (uri, defport); +#endif + + rc = (*ld == NULL) ? LDAP_SERVER_DOWN : LDAP_SUCCESS; + +#endif /* HAVE_LDAP_INITIALIZE */ + + stat = do_map_error (rc); + if (stat == NSS_STATUS_SUCCESS && *ld == NULL) + { + stat = NSS_STATUS_UNAVAIL; + } + return stat; +} + +static enum nss_status +do_init (void) +{ + struct ldap_config *cfg; +#ifndef HAVE_PTHREAD_ATFORK + pid_t pid; +#endif + uid_t euid; + enum nss_status stat; + int sd=-1; + + debug ("==> do_init"); + + if (_nss_ldap_validateconfig (__config) != NSS_STATUS_SUCCESS) + { + do_close (); + __config = NULL; + __session.ls_current_uri = 0; + } + +#ifndef HAVE_PTHREAD_ATFORK +#if defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H) + /* + * This bogosity is necessary because Linux uses different + * PIDs for different threads (like IRIX, which we don't + * support). We can tell whether we are linked against + * libpthreads by whether __pthread_once is NULL or + * not. If it is NULL, then we're not linked with the + * threading library, and we need to compare the current + * process ID against the saved one to figure out + * whether we've forked. + * + * Once we know whether we have forked or not, + * courtesy of pthread_atfork() or us checking + * ourselves, we can close the socket to the LDAP + * server to avoid leaking a socket, and reopen + * another connection. Under no circumstances do we + * wish to use the same connection, or to send an + * unbind PDU over the parents connection, as that + * will wreak all sorts of havoc or inefficiencies, + * respectively. + */ + if (__pthread_once == NULL) + pid = getpid (); + else + pid = -1; /* linked against libpthreads, don't care */ +#else + pid = getpid (); +#endif /* HAVE_LIBC_LOCK_H || HAVE_BITS_LIBC_LOCK_H */ +#endif /* HAVE_PTHREAD_ATFORK */ + + euid = geteuid (); + +#ifdef DEBUG +#ifdef HAVE_PTHREAD_ATFORK + syslog (LOG_AUTHPRIV | LOG_DEBUG, + "nss_ldap: __session.ls_state=%d, __session.ls_conn=%p, __euid=%i, euid=%i", + __session.ls_state, __session.ls_conn, __euid, euid); +#elif defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H) + syslog (LOG_AUTHPRIV | LOG_DEBUG, + "nss_ldap: libpthreads=%s, __session.ls_state=%d, __session.ls_conn=%p, __pid=%i, pid=%i, __euid=%i, euid=%i", + (__pthread_once == NULL ? "FALSE" : "TRUE"), + __session.ls_state, + (void *)__session.ls_conn, + (__pthread_once == NULL ? __pid : -1), + (__pthread_once == NULL ? pid : -1), __euid, euid); +#else + syslog (LOG_AUTHPRIV | LOG_DEBUG, + "nss_ldap: __session.ls_state=%d, __session.ls_conn=%p, __pid=%i, pid=%i, __euid=%i, euid=%i", + __session.ls_state, __session.ls_conn, __pid, pid, __euid, euid); +#endif +#endif /* DEBUG */ + + if (__session.ls_state == LS_CONNECTED_TO_DSA && + do_get_our_socket (&sd) == 0) + { + /* The calling app has stolen our socket. */ + debug (":== do_init (stolen socket detected)"); + do_drop_connection (sd, 0); + } + else +#ifndef HAVE_PTHREAD_ATFORK +#if defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H) + if (__pthread_once == NULL && __pid != pid) +#else + if (__pid != pid) +#endif /* HAVE_LIBC_LOCK_H || HAVE_BITS_LIBC_LOCK_H */ + { + do_close_no_unbind (); + } + else +#endif /* HAVE_PTHREAD_ATFORK */ + if (__euid != euid && (__euid == 0 || euid == 0)) + { + /* + * If we've changed user ids, close the session so we can + * rebind as the correct user. + */ + do_close (); + } + else if (__session.ls_state == LS_CONNECTED_TO_DSA) + { + time_t current_time; + + /* + * Otherwise we can hand back this process' global + * LDAP session. + * + * Patch from Steven Barrus <sbarrus@eng.utah.edu> to + * close the session after an idle timeout. + */ + + assert (__session.ls_conn != NULL); + assert (__session.ls_config != NULL); + + if (__session.ls_config->ldc_idle_timelimit) + { + time (¤t_time); + if ((__session.ls_timestamp + + __session.ls_config->ldc_idle_timelimit) < current_time) + { + debug ("idle_timelimit reached"); + do_close (); + } + } + + /* + * If the connection is still there (ie. do_close() wasn't + * called) then we can return the cached connection. + */ + if (__session.ls_state == LS_CONNECTED_TO_DSA) + { + debug ("<== do_init (cached session)"); + return NSS_STATUS_SUCCESS; + } + } + + __session.ls_conn = NULL; + __session.ls_timestamp = 0; + __session.ls_state = LS_UNINITIALIZED; + +#ifdef HAVE_PTHREAD_ATFORK + if (pthread_once (&__once, do_atfork_setup) != 0) + { + debug ("<== do_init (pthread_once failed)"); + return NSS_STATUS_UNAVAIL; + } +#elif defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H) + /* + * Only install the pthread_atfork() handlers i + * we are linked against libpthreads. Otherwise, + * do close the session when the PID changes. + */ + if (__pthread_once == NULL) + __pid = pid; + else + __libc_once (__once, do_atfork_setup); +#else + __pid = pid; +#endif + + __euid = euid; + + /* Initialize schema and LDAP handle (but do not connect) */ + if (__config == NULL) + { + char *configbufp = __configbuf; + size_t configbuflen = sizeof (__configbuf); + + stat = _nss_ldap_readconfig (&__config, &configbufp, &configbuflen); + if (stat == NSS_STATUS_NOTFOUND) + { + /* Config was read but no host information specified; try DNS */ + stat = _nss_ldap_mergeconfigfromdns (__config, &configbufp, &configbuflen); + } + + if (stat != NSS_STATUS_SUCCESS) + { + debug ("<== do_init (failed to read config)"); + return NSS_STATUS_UNAVAIL; + } + } + + cfg = __config; + + _nss_ldap_init_attributes (cfg->ldc_attrtab); + _nss_ldap_init_filters (); + +#ifdef HAVE_LDAP_SET_OPTION + if (cfg->ldc_debug) + { +#ifdef LBER_OPT_LOG_PRINT_FILE + if (cfg->ldc_logdir && !__debugfile) + { + char namebuf[PATH_MAX]; + + snprintf (namebuf, sizeof (namebuf), "%s/ldap.%d", cfg->ldc_logdir, + (int) getpid ()); + __debugfile = fopen (namebuf, "a"); + + if (__debugfile != NULL) + { + ber_set_option (NULL, LBER_OPT_LOG_PRINT_FILE, __debugfile); + } + } +#endif /* LBER_OPT_LOG_PRINT_FILE */ +#ifdef LBER_OPT_DEBUG_LEVEL + if (cfg->ldc_debug) + { + ber_set_option (NULL, LBER_OPT_DEBUG_LEVEL, &cfg->ldc_debug); + ldap_set_option (NULL, LDAP_OPT_DEBUG_LEVEL, &cfg->ldc_debug); + } +#endif /* LBER_OPT_DEBUG_LEVEL */ + } +#endif /* HAVE_LDAP_SET_OPTION */ + +#ifdef HAVE_LDAPSSL_CLIENT_INIT + /* + * Initialize the SSL library. + */ + if (cfg->ldc_ssl_on == SSL_LDAPS) + { + int rc = 0; + if (__ssl_initialized == 0 + && (rc = ldapssl_client_init (cfg->ldc_sslpath, NULL)) != LDAP_SUCCESS) + { + debug ("<== do_init (ldapssl_client_init failed with rc = %d)", rc); + return NSS_STATUS_UNAVAIL; + } + __ssl_initialized = 1; + } +#endif /* SSL */ + + __session.ls_conn = NULL; + + assert (__session.ls_current_uri <= NSS_LDAP_CONFIG_URI_MAX); + assert (cfg->ldc_uris[__session.ls_current_uri] != NULL); + + stat = do_init_session (&__session.ls_conn, + cfg->ldc_uris[__session.ls_current_uri], + cfg->ldc_port); + if (stat != NSS_STATUS_SUCCESS) + { + debug ("<== do_init (failed to initialize LDAP session)"); + return stat; + } + + __session.ls_config = cfg; + __session.ls_state = LS_INITIALIZED; + + debug ("<== do_init (initialized session)"); + + return NSS_STATUS_SUCCESS; +} + +/* + * A simple alias around do_init(). + */ +enum nss_status +_nss_ldap_init (void) +{ + return do_init (); +} + +/* + * A simple alias around do_close(). + */ +void +_nss_ldap_close (void) +{ + do_close (); +} + +#if defined(HAVE_LDAP_START_TLS_S) || defined(HAVE_LDAP_START_TLS) +static int +do_start_tls (struct ldap_session * session) +{ + int rc; +#ifdef HAVE_LDAP_START_TLS + int msgid; + struct timeval tv, *timeout; + LDAPMessage *res = NULL; + + debug ("==> do_start_tls"); + + rc = ldap_start_tls (session->ls_conn, NULL, NULL, &msgid); + if (rc != LDAP_SUCCESS) + { + debug ("<== do_start_tls (ldap_start_tls failed: %s)", ldap_err2string (rc)); + return rc; + } + + if (session->ls_config->ldc_bind_timelimit == LDAP_NO_LIMIT) + { + timeout = NULL; + } + else + { + tv.tv_sec = session->ls_config->ldc_bind_timelimit; + tv.tv_usec = 0; + timeout = &tv; + } + + rc = ldap_result (session->ls_conn, msgid, 1, timeout, &res); + if (rc == -1) + { +#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER) + if (ldap_get_option (session->ls_conn, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS) + { + rc = LDAP_UNAVAILABLE; + } +#else + rc = ld->ld_errno; +#endif /* LDAP_OPT_ERROR_NUMBER */ + + debug ("<== do_start_tls (ldap_start_tls failed: %s)", ldap_err2string (rc)); + return rc; + } + + rc = ldap_result2error (session->ls_conn, res, 1); + if (rc != LDAP_SUCCESS) + { + debug ("<== do_start_tls (ldap_result2error failed: %s)", ldap_err2string (rc)); + return rc; + } + + rc = ldap_install_tls (session->ls_conn); +#else + rc = ldap_start_tls_s (session->ls_conn, NULL, NULL); +#endif /* HAVE_LDAP_START_TLS */ + + if (rc != LDAP_SUCCESS) + { + debug ("<== do_start_tls (start TLS failed: %s)", ldap_err2string(rc)); + return rc; + } + + return LDAP_SUCCESS; +} +#endif + +/* + * Opens connection to an LDAP server - should only be called from search + * API. Other API that just needs access to configuration and schema should + * call do_init(). + * + * As with do_close(), this assumes ownership of sess. + * It also wants to own __config: is there a potential deadlock here? XXX + */ +static enum nss_status +do_open (void) +{ + struct ldap_config *cfg; + int usesasl; + char *bindarg; + enum nss_status stat; +#ifdef LDAP_OPT_NETWORK_TIMEOUT + struct timeval tv; +#endif +#ifdef LDAP_X_OPT_CONNECT_TIMEOUT + int timeout; +#endif + int rc; + + debug ("==> do_open"); + + /* Moved the head part of do_open() into do_init() */ + stat = do_init (); + if (stat != NSS_STATUS_SUCCESS) + { + debug ("<== do_open (session initialization failed)"); + return stat; + } + + assert (__session.ls_conn != NULL); + assert (__session.ls_config != NULL); + assert (__session.ls_state != LS_UNINITIALIZED); + + if (__session.ls_state == LS_CONNECTED_TO_DSA) + { + debug ("<== do_open (cached session)"); + return NSS_STATUS_SUCCESS; + } + + cfg = __session.ls_config; + +#if LDAP_SET_REBIND_PROC_ARGS == 3 + ldap_set_rebind_proc (__session.ls_conn, do_rebind, NULL); +#elif LDAP_SET_REBIND_PROC_ARGS == 2 + ldap_set_rebind_proc (__session.ls_conn, do_rebind); +#endif + + ldap_set_option (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION, + &cfg->ldc_version); + + ldap_set_option (__session.ls_conn, LDAP_OPT_DEREF, &cfg->ldc_deref); + + ldap_set_option (__session.ls_conn, LDAP_OPT_TIMELIMIT, + &cfg->ldc_timelimit); + +#ifdef LDAP_X_OPT_CONNECT_TIMEOUT + /* + * This is a new option in the Netscape SDK which sets + * the TCP connect timeout. For want of a better value, + * we use the bind_timelimit to control this. + */ + timeout = cfg->ldc_bind_timelimit * 1000; + ldap_set_option (__session.ls_conn, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout); +#endif /* LDAP_X_OPT_CONNECT_TIMEOUT */ + +#ifdef LDAP_OPT_NETWORK_TIMEOUT + tv.tv_sec = cfg->ldc_bind_timelimit; + tv.tv_usec = 0; + ldap_set_option (__session.ls_conn, LDAP_OPT_NETWORK_TIMEOUT, &tv); +#endif /* LDAP_OPT_NETWORK_TIMEOUT */ + +#ifdef LDAP_OPT_REFERRALS + ldap_set_option (__session.ls_conn, LDAP_OPT_REFERRALS, + cfg->ldc_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF); +#endif /* LDAP_OPT_REFERRALS */ + +#ifdef LDAP_OPT_RESTART + ldap_set_option (__session.ls_conn, LDAP_OPT_RESTART, + cfg->ldc_restart ? LDAP_OPT_ON : LDAP_OPT_OFF); +#endif /* LDAP_OPT_RESTART */ + +#if defined(HAVE_LDAP_START_TLS_S) || defined(HAVE_LDAP_START_TLS) + if (cfg->ldc_ssl_on == SSL_START_TLS) + { + int version; + + if (ldap_get_option + (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION, + &version) == LDAP_OPT_SUCCESS) + { + if (version < LDAP_VERSION3) + { + version = LDAP_VERSION3; + ldap_set_option (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION, + &version); + } + } + + /* set up SSL context */ + if (do_ssl_options (cfg) != LDAP_SUCCESS) + { + do_close (); + debug ("<== do_open (SSL setup failed)"); + return NSS_STATUS_UNAVAIL; + } + + stat = do_map_error (do_start_tls (&__session)); + if (stat == NSS_STATUS_SUCCESS) + { + debug (":== do_open (TLS startup succeeded)"); + } + else + { + do_close (); + debug ("<== do_open (TLS startup failed)"); + return stat; + } + } + else +#endif /* HAVE_LDAP_START_TLS_S || HAVE_LDAP_START_TLS */ + + /* + * If SSL is desired, then enable it. + */ + if (cfg->ldc_ssl_on == SSL_LDAPS) + { +#if defined(LDAP_OPT_X_TLS) + int tls = LDAP_OPT_X_TLS_HARD; + if (ldap_set_option (__session.ls_conn, LDAP_OPT_X_TLS, &tls) != + LDAP_SUCCESS) + { + do_close (); + debug ("<== do_open (TLS setup failed)"); + return NSS_STATUS_UNAVAIL; + } + + /* set up SSL context */ + if (do_ssl_options (cfg) != LDAP_SUCCESS) + { + do_close (); + debug ("<== do_open (SSL setup failed)"); + return NSS_STATUS_UNAVAIL; + } + +#elif defined(HAVE_LDAPSSL_CLIENT_INIT) + if (ldapssl_install_routines (__session.ls_conn) != LDAP_SUCCESS) + { + do_close (); + debug ("<== do_open (SSL setup failed)"); + return NSS_STATUS_UNAVAIL; + } +/* not in Solaris 9? */ +#ifndef LDAP_OPT_SSL +#define LDAP_OPT_SSL 0x0A +#endif + if (ldap_set_option (__session.ls_conn, LDAP_OPT_SSL, LDAP_OPT_ON) != + LDAP_SUCCESS) + { + do_close (); + debug ("<== do_open (SSL setup failed)"); + return NSS_STATUS_UNAVAIL; + } +#endif + } + + /* + * If we're running as root, let us bind as a special + * user, so we can fake shadow passwords. + * Thanks to Doug Nazar <nazard@dragoninc.on.ca> for this + * patch. + */ + if (__euid == 0 && cfg->ldc_rootbinddn != NULL) + { +#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H)) + usesasl = cfg->ldc_rootusesasl; + bindarg = + cfg->ldc_rootusesasl ? cfg->ldc_rootsaslid : cfg->ldc_rootbindpw; +#else + usesasl = 0; + bindarg = cfg->ldc_rootbindpw; +#endif + + rc = do_bind (__session.ls_conn, + cfg->ldc_bind_timelimit, + cfg->ldc_rootbinddn, bindarg, usesasl); + } + else + { +#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H)) + usesasl = cfg->ldc_usesasl; + bindarg = cfg->ldc_usesasl ? cfg->ldc_saslid : cfg->ldc_bindpw; +#else + usesasl = 0; + bindarg = cfg->ldc_bindpw; +#endif + + rc = do_bind (__session.ls_conn, + cfg->ldc_bind_timelimit, + cfg->ldc_binddn, + cfg->ldc_bindpw, usesasl); + } + + if (rc != LDAP_SUCCESS) + { + /* log actual LDAP error code */ + syslog (LOG_AUTHPRIV | LOG_INFO, + "nss_ldap: failed to bind to LDAP server %s: %s", + cfg->ldc_uris[__session.ls_current_uri], + ldap_err2string (rc)); + stat = do_map_error (rc); + do_close (); + debug ("<== do_open (failed to bind to DSA"); + } + else + { + do_set_sockopts (); + time (&__session.ls_timestamp); + __session.ls_state = LS_CONNECTED_TO_DSA; + stat = NSS_STATUS_SUCCESS; + debug ("<== do_open (session connected to DSA)"); + } + + return stat; +} + +#if defined HAVE_LDAP_START_TLS_S || (defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS)) +static int +do_ssl_options (struct ldap_config * cfg) +{ + int rc; + + debug ("==> do_ssl_options"); + +#ifdef LDAP_OPT_X_TLS_RANDOM_FILE + if (cfg->ldc_tls_randfile != NULL) + { + /* rand file */ + rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_RANDOM_FILE, + cfg->ldc_tls_randfile); + if (rc != LDAP_SUCCESS) + { + debug + ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_RANDOM_FILE failed"); + return LDAP_OPERATIONS_ERROR; + } + } +#endif /* LDAP_OPT_X_TLS_RANDOM_FILE */ + + if (cfg->ldc_tls_cacertfile != NULL) + { + /* ca cert file */ + rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, + cfg->ldc_tls_cacertfile); + if (rc != LDAP_SUCCESS) + { + debug + ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_CACERTFILE failed"); + return LDAP_OPERATIONS_ERROR; + } + } + + if (cfg->ldc_tls_cacertdir != NULL) + { + /* ca cert directory */ + rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTDIR, + cfg->ldc_tls_cacertdir); + if (rc != LDAP_SUCCESS) + { + debug + ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_CACERTDIR failed"); + return LDAP_OPERATIONS_ERROR; + } + } + + /* require cert? */ + if (cfg->ldc_tls_checkpeer > -1) + { + rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, + &cfg->ldc_tls_checkpeer); + if (rc != LDAP_SUCCESS) + { + debug + ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_REQUIRE_CERT failed"); + return LDAP_OPERATIONS_ERROR; + } + } + + if (cfg->ldc_tls_ciphers != NULL) + { + /* set cipher suite, certificate and private key: */ + rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, + cfg->ldc_tls_ciphers); + if (rc != LDAP_SUCCESS) + { + debug + ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_CIPHER_SUITE failed"); + return LDAP_OPERATIONS_ERROR; + } + } + + if (cfg->ldc_tls_cert != NULL) + { + rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CERTFILE, cfg->ldc_tls_cert); + if (rc != LDAP_SUCCESS) + { + debug + ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_CERTFILE failed"); + return LDAP_OPERATIONS_ERROR; + } + } + + if (cfg->ldc_tls_key != NULL) + { + rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_KEYFILE, cfg->ldc_tls_key); + if (rc != LDAP_SUCCESS) + { + debug + ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_KEYFILE failed"); + return LDAP_OPERATIONS_ERROR; + } + } + + debug ("<== do_ssl_options"); + + return LDAP_SUCCESS; +} +#endif + +/* + * This function initializes an enumeration context, acquiring + * the global mutex. + * + * It could be done from the default constructor, under Solaris, but we + * delay it until the setXXent() function is called. + */ +struct ent_context * +_nss_ldap_ent_context_init (struct ent_context ** pctx) +{ + struct ent_context *ctx; + + _nss_ldap_enter (); + + ctx = _nss_ldap_ent_context_init_locked (pctx); + + _nss_ldap_leave (); + + return ctx; +} + +/* + * Wrapper around ldap_result() to skip over search references + * and deal transparently with the last entry. + */ +static enum nss_status +do_result (struct ent_context * ctx, int all) +{ + int rc = LDAP_UNAVAILABLE; + enum nss_status stat = NSS_STATUS_TRYAGAIN; + struct timeval tv, *tvp; + + debug ("==> do_result"); + + if (__session.ls_config->ldc_timelimit == LDAP_NO_LIMIT) + { + tvp = NULL; + } + else + { + tv.tv_sec = __session.ls_config->ldc_timelimit; + tv.tv_usec = 0; + tvp = &tv; + } + + do + { + if (ctx->ec_res != NULL) + { + ldap_msgfree (ctx->ec_res); + ctx->ec_res = NULL; + } + + rc = + ldap_result (__session.ls_conn, ctx->ec_msgid, all, tvp, + &ctx->ec_res); + switch (rc) + { + case -1: + case 0: + if (ldap_get_option + (__session.ls_conn, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS) + { + rc = LDAP_UNAVAILABLE; + } + syslog (LOG_AUTHPRIV | LOG_ERR, "nss_ldap: could not get LDAP result - %s", + ldap_err2string (rc)); + stat = NSS_STATUS_UNAVAIL; + break; + case LDAP_RES_SEARCH_ENTRY: + stat = NSS_STATUS_SUCCESS; + break; + case LDAP_RES_SEARCH_RESULT: + if (all == LDAP_MSG_ALL) + { + /* we asked for the result chain, we got it. */ + stat = NSS_STATUS_SUCCESS; + } + else + { +#ifdef LDAP_MORE_RESULTS_TO_RETURN + int parserc; + /* NB: this frees ctx->ec_res */ + LDAPControl **resultControls = NULL; + + ctx->ec_cookie = NULL; + + parserc = + ldap_parse_result (__session.ls_conn, ctx->ec_res, &rc, NULL, + NULL, NULL, &resultControls, 1); + if (parserc != LDAP_SUCCESS + && parserc != LDAP_MORE_RESULTS_TO_RETURN) + { + stat = NSS_STATUS_UNAVAIL; + ldap_abandon (__session.ls_conn, ctx->ec_msgid); + syslog (LOG_AUTHPRIV | LOG_ERR, + "nss_ldap: could not get LDAP result - %s", + ldap_err2string (rc)); + } + else if (resultControls != NULL) + { + /* See if there are any more pages to come */ + parserc = ldap_parse_page_control (__session.ls_conn, + resultControls, NULL, + &(ctx->ec_cookie)); + ldap_controls_free (resultControls); + stat = NSS_STATUS_NOTFOUND; + } + else + { + stat = NSS_STATUS_NOTFOUND; + } +#else + stat = NSS_STATUS_NOTFOUND; +#endif /* LDAP_MORE_RESULTS_TO_RETURN */ + ctx->ec_res = NULL; + ctx->ec_msgid = -1; + } + break; + default: + stat = NSS_STATUS_UNAVAIL; + break; + } + } +#ifdef LDAP_RES_SEARCH_REFERENCE + while (rc == LDAP_RES_SEARCH_REFERENCE); +#else + while (0); +#endif /* LDAP_RES_SEARCH_REFERENCE */ + + if (stat == NSS_STATUS_SUCCESS) + time (&__session.ls_timestamp); + + debug ("<== do_result"); + + return stat; +} + +/* + * This function initializes an enumeration context. + * + * It could be done from the default constructor, under Solaris, but we + * delay it until the setXXent() function is called. + */ +struct ent_context * +_nss_ldap_ent_context_init_locked (struct ent_context ** pctx) +{ + struct ent_context *ctx; + + debug ("==> _nss_ldap_ent_context_init_locked"); + + ctx = *pctx; + + if (ctx == NULL) + { + ctx = (struct ent_context *) malloc (sizeof (*ctx)); + if (ctx == NULL) + { + debug ("<== _nss_ldap_ent_context_init_locked"); + return NULL; + } + *pctx = ctx; + } + else + { + if (ctx->ec_res != NULL) + { + ldap_msgfree (ctx->ec_res); + } + if (ctx->ec_cookie != NULL) + { + ber_bvfree (ctx->ec_cookie); + } + if (ctx->ec_msgid > -1 && do_result (ctx, LDAP_MSG_ONE) == NSS_STATUS_SUCCESS) + { + ldap_abandon (__session.ls_conn, ctx->ec_msgid); + } + } + + ctx->ec_cookie = NULL; + ctx->ec_res = NULL; + ctx->ec_msgid = -1; + ctx->ec_sd = NULL; + + LS_INIT (ctx->ec_state); + + debug ("<== _nss_ldap_ent_context_init_locked"); + + return ctx; +} + +/* + * Clears a given context; we require the caller + * to acquire the lock. + */ +void +_nss_ldap_ent_context_release (struct ent_context * ctx) +{ + debug ("==> _nss_ldap_ent_context_release"); + + if (ctx == NULL) + { + debug ("<== _nss_ldap_ent_context_release"); + return; + } + + if (ctx->ec_res != NULL) + { + ldap_msgfree (ctx->ec_res); + ctx->ec_res = NULL; + } + + /* + * Abandon the search if there were more results to fetch. + */ + if (ctx->ec_msgid > -1 && do_result (ctx, LDAP_MSG_ONE) == NSS_STATUS_SUCCESS) + { + ldap_abandon (__session.ls_conn, ctx->ec_msgid); + ctx->ec_msgid = -1; + } + + if (ctx->ec_cookie != NULL) + { + ber_bvfree (ctx->ec_cookie); + ctx->ec_cookie = NULL; + } + + ctx->ec_sd = NULL; + + LS_INIT (ctx->ec_state); + + if (_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_CONNECT_POLICY_ONESHOT)) + { + do_close (); + } + + debug ("<== _nss_ldap_ent_context_release"); + + return; +} + +/* + * AND or OR a set of filters. + */ +static enum nss_status +do_aggregate_filter (const char **values, + enum ldap_args_types type, + const char *filterprot, char *bufptr, size_t buflen) +{ + enum nss_status stat; + const char **valueP; + + assert (buflen > sizeof ("(|)")); + + bufptr[0] = '('; + bufptr[1] = (type == LA_TYPE_STRING_LIST_AND) ? '&' : '|'; + + bufptr += 2; + buflen -= 2; + + for (valueP = values; *valueP != NULL; valueP++) + { + size_t len; + char filter[LDAP_FILT_MAXSIZ], escapedBuf[LDAP_FILT_MAXSIZ]; + + stat = + _nss_ldap_escape_string (*valueP, escapedBuf, sizeof (escapedBuf)); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + snprintf (filter, sizeof (filter), filterprot, escapedBuf); + len = strlen (filter); + + if (buflen < len + 1 /* ')' */ ) + return NSS_STATUS_TRYAGAIN; + + memcpy (bufptr, filter, len); + bufptr[len] = '\0'; + bufptr += len; + buflen -= len; + } + + if (buflen < 2) + return NSS_STATUS_TRYAGAIN; + + *bufptr++ = ')'; + *bufptr++ = '\0'; + + buflen -= 2; + + return NSS_STATUS_SUCCESS; +} + +/* + * Do the necessary formatting to create a string filter. + */ +static enum nss_status +do_filter (const struct ldap_args * args, const char *filterprot, + struct ldap_service_search_descriptor * sd, char *userBuf, + size_t userBufSiz, char **dynamicUserBuf, const char **retFilter) +{ + char buf1[LDAP_FILT_MAXSIZ], buf2[LDAP_FILT_MAXSIZ]; + char *filterBufP, filterBuf[LDAP_FILT_MAXSIZ]; + size_t filterSiz; + enum nss_status stat = NSS_STATUS_SUCCESS; + + debug ("==> do_filter"); + + *dynamicUserBuf = NULL; + + if (args != NULL && args->la_type != LA_TYPE_NONE) + { + /* choose what to use for temporary storage */ + + if (sd != NULL && sd->lsd_filter != NULL) + { + filterBufP = filterBuf; + filterSiz = sizeof (filterBuf); + } + else + { + filterBufP = userBuf; + filterSiz = userBufSiz; + } + + switch (args->la_type) + { + case LA_TYPE_STRING: + stat = _nss_ldap_escape_string (args->la_arg1.la_string, buf1, + sizeof (buf1)); + if (stat != NSS_STATUS_SUCCESS) + break; + + snprintf (filterBufP, filterSiz, filterprot, buf1); + break; + case LA_TYPE_NUMBER: + snprintf (filterBufP, filterSiz, filterprot, + args->la_arg1.la_number); + break; + case LA_TYPE_STRING_AND_STRING: + stat = _nss_ldap_escape_string (args->la_arg1.la_string, buf1, + sizeof (buf1)); + if (stat != NSS_STATUS_SUCCESS) + break; + + stat = _nss_ldap_escape_string (args->la_arg2.la_string, buf2, + sizeof (buf2)); + if (stat != NSS_STATUS_SUCCESS) + break; + + snprintf (filterBufP, filterSiz, filterprot, buf1, buf2); + break; + case LA_TYPE_NUMBER_AND_STRING: + stat = _nss_ldap_escape_string (args->la_arg2.la_string, buf1, + sizeof (buf1)); + if (stat != NSS_STATUS_SUCCESS) + break; + + snprintf (filterBufP, filterSiz, filterprot, + args->la_arg1.la_number, buf1); + break; + case LA_TYPE_STRING_LIST_OR: + case LA_TYPE_STRING_LIST_AND: + do + { + stat = do_aggregate_filter (args->la_arg1.la_string_list, + args->la_type, + filterprot, filterBufP, filterSiz); + if (stat == NSS_STATUS_TRYAGAIN) + { + filterBufP = *dynamicUserBuf = realloc (*dynamicUserBuf, + 2 * filterSiz); + if (filterBufP == NULL) + return NSS_STATUS_UNAVAIL; + filterSiz *= 2; + } + } + while (stat == NSS_STATUS_TRYAGAIN); + break; + default: + return NSS_STATUS_UNAVAIL; + break; + } + + if (stat != NSS_STATUS_SUCCESS) + return stat; + + /* + * This code really needs to be cleaned up. + */ + if (sd != NULL && sd->lsd_filter != NULL) + { + size_t filterBufPLen = strlen (filterBufP); + + /* remove trailing bracket */ + if (filterBufP[filterBufPLen - 1] == ')') + filterBufP[filterBufPLen - 1] = '\0'; + + if (*dynamicUserBuf != NULL) + { + char *oldDynamicUserBuf = *dynamicUserBuf; + size_t dynamicUserBufSiz; + + dynamicUserBufSiz = filterBufPLen + strlen (sd->lsd_filter) + + sizeof ("())"); + *dynamicUserBuf = malloc (dynamicUserBufSiz); + if (*dynamicUserBuf == NULL) + { + free (oldDynamicUserBuf); + return NSS_STATUS_UNAVAIL; + } + + snprintf (*dynamicUserBuf, dynamicUserBufSiz, "%s(%s))", + filterBufP, sd->lsd_filter); + free (oldDynamicUserBuf); + } + else + { + snprintf (userBuf, userBufSiz, "%s(%s))", + filterBufP, sd->lsd_filter); + } + } + + if (*dynamicUserBuf != NULL) + *retFilter = *dynamicUserBuf; + else + *retFilter = userBuf; + } + else + { + /* no arguments, probably an enumeration filter */ + if (sd != NULL && sd->lsd_filter != NULL) + { + snprintf (userBuf, userBufSiz, "(&%s(%s))", + filterprot, sd->lsd_filter); + *retFilter = userBuf; + } + else + { + *retFilter = filterprot; + } + } + + debug (":== do_filter: %s", *retFilter); + + debug ("<== do_filter"); + + return NSS_STATUS_SUCCESS; +} + +/* + * Function to call either do_search() or do_search_s() with + * reconnection logic. + */ +static enum nss_status +do_with_reconnect (const char *base, int scope, + const char *filter, const char **attrs, int sizelimit, + void *private, search_func_t search_func) +{ + int rc = LDAP_UNAVAILABLE, tries = 0, backoff = 0; + int hard = 1, start_uri = 0, log = 0; + enum nss_status stat = NSS_STATUS_UNAVAIL; + int maxtries; + + debug ("==> do_with_reconnect"); + + /* caller must successfully call do_init() first */ + assert (__session.ls_config != NULL); + + maxtries = __session.ls_config->ldc_reconnect_maxconntries + + __session.ls_config->ldc_reconnect_tries; + + while (stat == NSS_STATUS_UNAVAIL && hard && tries < maxtries) + { + if (tries >= __session.ls_config->ldc_reconnect_maxconntries) + { + if (backoff == 0) + backoff = __session.ls_config->ldc_reconnect_sleeptime; + else if (backoff < __session.ls_config->ldc_reconnect_maxsleeptime) + backoff *= 2; + + syslog (LOG_AUTHPRIV | LOG_INFO, + "nss_ldap: reconnecting to LDAP server (sleeping %d seconds)...", + backoff); + (void) sleep (backoff); + } + else if (tries > 1) + { + /* Don't sleep, reconnect immediately. */ + syslog (LOG_AUTHPRIV | LOG_INFO, "nss_ldap: reconnecting to LDAP server..."); + } + + /* For each "try", attempt to connect to all specified URIs */ + start_uri = __session.ls_current_uri; + do + { + stat = do_open (); + if (stat == NSS_STATUS_SUCCESS) + { + stat = do_map_error (search_func (base, scope, filter, + attrs, sizelimit, private)); + } + if (stat != NSS_STATUS_UNAVAIL) + break; + + log++; + + /* test in case config file could not be read */ + if (__session.ls_config != NULL) + { + assert (__session.ls_config-> + ldc_uris[__session.ls_current_uri] != NULL); + + __session.ls_current_uri++; + + if (__session.ls_config->ldc_uris[__session.ls_current_uri] == + NULL) + __session.ls_current_uri = 0; + } + } + while (__session.ls_current_uri != start_uri); + + if (stat == NSS_STATUS_UNAVAIL) + { + do_close (); + + /* + * If a soft reconnect policy is specified, then do not + * try to reconnect to the LDAP server if it is down. + */ + if (__session.ls_config->ldc_reconnect_pol == LP_RECONNECT_SOFT) + hard = 0; + + /* + * If the file /lib/init/rw/libnss-ldap.bind_policy_soft exists, + * then ignore the actual bind_policy definition and use the + * soft semantics. This file should only exist during early + * boot and late shutdown, points at which the networking or + * the LDAP server itself are likely to be unavailable anyway. + */ + if (access("/lib/init/rw/libnss-ldap.bind_policy_soft",R_OK) == 0) + hard = 0; + + ++tries; + } + } + + switch (stat) + { + case NSS_STATUS_UNAVAIL: + syslog (LOG_AUTHPRIV | LOG_ERR, "nss_ldap: could not search LDAP server - %s", + ldap_err2string (rc)); + break; + case NSS_STATUS_TRYAGAIN: + syslog (LOG_AUTHPRIV | LOG_ERR, + "nss_ldap: could not %s %sconnect to LDAP server - %s", + hard ? "hard" : "soft", tries ? "re" : "", + ldap_err2string (rc)); + stat = NSS_STATUS_UNAVAIL; + break; + case NSS_STATUS_SUCCESS: + if (log) + { + char *uri = __session.ls_config->ldc_uris[__session.ls_current_uri]; + + if (uri == NULL) + uri = "(null)"; + + if (tries) + syslog (LOG_AUTHPRIV | LOG_INFO, + "nss_ldap: reconnected to LDAP server %s after %d attempt%s", + uri, tries, (tries == 1) ? "" : "s"); + else + syslog (LOG_AUTHPRIV | LOG_INFO, "nss_ldap: reconnected to LDAP server %s", uri); + } + time (&__session.ls_timestamp); + break; + default: + break; + } + + debug ("<== do_with_reconnect"); + return stat; +} + +/* + * Synchronous search function. Don't call this directly; + * always wrap calls to this with do_with_reconnect(), or, + * better still, use _nss_ldap_search_s(). + */ +static int +do_search_s (const char *base, int scope, + const char *filter, const char **attrs, int sizelimit, + LDAPMessage ** res) +{ + int rc; + struct timeval tv, *tvp; + + debug ("==> do_search_s"); + + ldap_set_option (__session.ls_conn, LDAP_OPT_SIZELIMIT, + (void *) &sizelimit); + + if (__session.ls_config->ldc_timelimit == LDAP_NO_LIMIT) + { + tvp = NULL; + } + else + { + tv.tv_sec = __session.ls_config->ldc_timelimit; + tv.tv_usec = 0; + tvp = &tv; + } + + rc = ldap_search_st (__session.ls_conn, base, scope, filter, + (char **) attrs, 0, tvp, res); + + debug ("<== do_search_s"); + + return rc; +} + +/* + * Asynchronous search function. Don't call this directly; + * always wrap calls to this with do_with_reconnect(), or, + * better still, use _nss_ldap_search(). + */ +static int +do_search (const char *base, int scope, + const char *filter, const char **attrs, int sizelimit, int *msgid) +{ + int rc; + LDAPControl *serverCtrls[2]; + LDAPControl **pServerCtrls; + + debug ("==> do_search"); + +#ifdef HAVE_LDAP_SEARCH_EXT + if (_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_PAGED_RESULTS)) + { + rc = ldap_create_page_control (__session.ls_conn, + __session.ls_config->ldc_pagesize, + NULL, 0, &serverCtrls[0]); + if (rc != LDAP_SUCCESS) + return rc; + + serverCtrls[1] = NULL; + pServerCtrls = serverCtrls; + } + else + { + pServerCtrls = NULL; + } + + rc = ldap_search_ext (__session.ls_conn, base, scope, filter, + (char **) attrs, 0, pServerCtrls, NULL, + LDAP_NO_LIMIT, sizelimit, msgid); + + if (pServerCtrls != NULL) + { + ldap_control_free (serverCtrls[0]); + serverCtrls[0] = NULL; + } + +#else + ldap_set_option (__session.ls_conn, LDAP_OPT_SIZELIMIT, + (void *) &sizelimit); + + *msgid = ldap_search (__session.ls_conn, base, scope, filter, + (char **) attrs, 0); + if (*msgid < 0) + { + if (ldap_get_option + (__session.ls_conn, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS) + { + rc = LDAP_UNAVAILABLE; + } + } + else + { + rc = LDAP_SUCCESS; + } +#endif /* HAVE_LDAP_SEARCH_EXT */ + + debug ("<== do_search"); + + return rc; +} + +static void +do_map_errno (enum nss_status status, int *errnop) +{ + switch (status) + { + case NSS_STATUS_TRYAGAIN: + *errnop = ERANGE; + break; + case NSS_STATUS_NOTFOUND: + *errnop = ENOENT; + break; + case NSS_STATUS_SUCCESS: + default: + *errnop = 0; + } +} + +/* + * Tries parser function "parser" on entries, calling do_result() + * to retrieve them from the LDAP server until one parses + * correctly or there is an exceptional condition. + */ +static enum nss_status +do_parse (struct ent_context * ctx, void *result, char + *buffer, size_t buflen, int *errnop, parser_t parser) +{ + enum nss_status parseStat = NSS_STATUS_NOTFOUND; + + debug ("==> do_parse"); + + /* + * if ec_state.ls_info.ls_index is non-zero, then we don't collect another + * entry off the LDAP chain, and instead refeed the existing result to + * the parser. Once the parser has finished with it, it will return + * NSS_STATUS_NOTFOUND and reset the index to -1, at which point we'll retrieve + * another entry. + */ + do + { + enum nss_status resultStat = NSS_STATUS_SUCCESS; + + if (ctx->ec_state.ls_retry == 0 && + (ctx->ec_state.ls_type == LS_TYPE_KEY + || ctx->ec_state.ls_info.ls_index == -1)) + { + resultStat = do_result (ctx, LDAP_MSG_ONE); + } + + if (resultStat != NSS_STATUS_SUCCESS) + { + /* Could not get a result; bail */ + parseStat = resultStat; + break; + } + + /* + * We have an entry; now, try to parse it. + * + * If we do not parse the entry because of a schema + * violation, the parser should return NSS_STATUS_NOTFOUND. + * We'll keep on trying subsequent entries until we + * find one which is parseable, or exhaust avialable + * entries, whichever is first. + */ + parseStat = parser (ctx->ec_res, &ctx->ec_state, result, + buffer, buflen); + + /* hold onto the state if we're out of memory XXX */ + ctx->ec_state.ls_retry = (parseStat == NSS_STATUS_TRYAGAIN && buffer != NULL ? 1 : 0); + + /* free entry is we're moving on */ + if (ctx->ec_state.ls_retry == 0 && + (ctx->ec_state.ls_type == LS_TYPE_KEY + || ctx->ec_state.ls_info.ls_index == -1)) + { + /* we don't need the result anymore, ditch it. */ + ldap_msgfree (ctx->ec_res); + ctx->ec_res = NULL; + } + } + while (parseStat == NSS_STATUS_NOTFOUND); + + do_map_errno (parseStat, errnop); + + debug ("<== do_parse"); + + return parseStat; +} + +/* + * Parse, fetching reuslts from chain instead of server. + */ +static enum nss_status +do_parse_s (struct ent_context * ctx, void *result, char + *buffer, size_t buflen, int *errnop, parser_t parser) +{ + enum nss_status parseStat = NSS_STATUS_NOTFOUND; + LDAPMessage *e = NULL; + + debug ("==> do_parse_s"); + + /* + * if ec_state.ls_info.ls_index is non-zero, then we don't collect another + * entry off the LDAP chain, and instead refeed the existing result to + * the parser. Once the parser has finished with it, it will return + * NSS_STATUS_NOTFOUND and reset the index to -1, at which point we'll retrieve + * another entry. + */ + do + { + if (ctx->ec_state.ls_retry == 0 && + (ctx->ec_state.ls_type == LS_TYPE_KEY + || ctx->ec_state.ls_info.ls_index == -1)) + { + if (e == NULL) + e = ldap_first_entry (__session.ls_conn, ctx->ec_res); + else + e = ldap_next_entry (__session.ls_conn, e); + } + + if (e == NULL) + { + /* Could not get a result; bail */ + parseStat = NSS_STATUS_NOTFOUND; + break; + } + + /* + * We have an entry; now, try to parse it. + * + * If we do not parse the entry because of a schema + * violation, the parser should return NSS_STATUS_NOTFOUND. + * We'll keep on trying subsequent entries until we + * find one which is parseable, or exhaust avialable + * entries, whichever is first. + */ + parseStat = parser (e, &ctx->ec_state, result, buffer, buflen); + + /* hold onto the state if we're out of memory XXX */ + ctx->ec_state.ls_retry = (parseStat == NSS_STATUS_TRYAGAIN && buffer != NULL ? 1 : 0); + } + while (parseStat == NSS_STATUS_NOTFOUND); + + do_map_errno (parseStat, errnop); + + debug ("<== do_parse_s"); + + return parseStat; +} + +/* + * Read an entry from the directory, a la X.500. This is used + * for functions that need to retrieve attributes from a DN, + * such as the RFC2307bis group expansion function. + */ +enum nss_status +_nss_ldap_read (const char *dn, const char **attributes, LDAPMessage ** res) +{ + return do_with_reconnect (dn, LDAP_SCOPE_BASE, "(objectclass=*)", + attributes, 1, /* sizelimit */ res, + (search_func_t) do_search_s); +} + +/* + * Simple wrapper around ldap_get_values(). Requires that + * session is already established. + */ +char ** +_nss_ldap_get_values (LDAPMessage * e, const char *attr) +{ + if (__session.ls_state != LS_CONNECTED_TO_DSA) + { + return NULL; + } + assert (__session.ls_conn != NULL); + + return ldap_get_values (__session.ls_conn, e, (char *) attr); +} + +/* + * Simple wrapper around ldap_get_dn(). Requires that + * session is already established. + */ +char * +_nss_ldap_get_dn (LDAPMessage * e) +{ + if (__session.ls_state != LS_CONNECTED_TO_DSA) + { + return NULL; + } + assert (__session.ls_conn != NULL); + + return ldap_get_dn (__session.ls_conn, e); +} + +/* + * Simple wrapper around ldap_first_entry(). Requires that + * session is already established. + */ +LDAPMessage * +_nss_ldap_first_entry (LDAPMessage * res) +{ + if (__session.ls_state != LS_CONNECTED_TO_DSA) + { + return NULL; + } + assert (__session.ls_conn != NULL); + + return ldap_first_entry (__session.ls_conn, res); +} + +/* + * Simple wrapper around ldap_next_entry(). Requires that + * session is already established. + */ +LDAPMessage * +_nss_ldap_next_entry (LDAPMessage * res) +{ + if (__session.ls_state != LS_CONNECTED_TO_DSA) + { + return NULL; + } + assert (__session.ls_conn != NULL); + + return ldap_next_entry (__session.ls_conn, res); +} + +char * +_nss_ldap_first_attribute (LDAPMessage * entry, BerElement ** berptr) +{ + if (__session.ls_state != LS_CONNECTED_TO_DSA) + { + return NULL; + } + assert (__session.ls_conn != NULL); + + return ldap_first_attribute (__session.ls_conn, entry, berptr); +} + +char * +_nss_ldap_next_attribute (LDAPMessage * entry, BerElement * ber) +{ + if (__session.ls_state != LS_CONNECTED_TO_DSA) + { + return NULL; + } + assert (__session.ls_conn != NULL); + + return ldap_next_attribute (__session.ls_conn, entry, ber); +} + +/* + * The generic synchronous lookup cover function. + * Assumes caller holds lock. + */ +enum nss_status +_nss_ldap_search_s (const struct ldap_args * args, + const char *filterprot, enum ldap_map_selector sel, const + char **user_attrs, int sizelimit, LDAPMessage ** res) +{ + char sdBase[LDAP_FILT_MAXSIZ]; + const char *base = NULL; + char filterBuf[LDAP_FILT_MAXSIZ], *dynamicFilterBuf = NULL; + const char **attrs, *filter; + int scope; + enum nss_status stat; + struct ldap_service_search_descriptor *sd = NULL; + + debug ("==> _nss_ldap_search_s"); + + stat = do_init (); + if (stat != NSS_STATUS_SUCCESS) + { + debug ("<== _nss_ldap_search_s"); + return stat; + } + + /* Set some reasonable defaults. */ + base = __session.ls_config->ldc_base; + scope = __session.ls_config->ldc_scope; + attrs = NULL; + + if (args != NULL && args->la_base != NULL) + { + sel = LM_NONE; + base = args->la_base; + } + + if (sel < LM_NONE) + { + sd = __session.ls_config->ldc_sds[sel]; + next: + if (sd != NULL) + { + size_t len = strlen (sd->lsd_base); + if (sd->lsd_base[len - 1] == ',') + { + /* is relative */ + snprintf (sdBase, sizeof (sdBase), + "%s%s", sd->lsd_base, + __session.ls_config->ldc_base); + base = sdBase; + } + else + { + base = sd->lsd_base; + } + + if (sd->lsd_scope != -1) + { + scope = sd->lsd_scope; + } + } + attrs = __session.ls_config->ldc_attrtab[sel]; + } + + stat = + do_filter (args, filterprot, sd, filterBuf, sizeof (filterBuf), + &dynamicFilterBuf, &filter); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = do_with_reconnect (base, scope, filter, + (user_attrs != NULL) ? user_attrs : attrs, + sizelimit, res, (search_func_t) do_search_s); + + if (dynamicFilterBuf != NULL) + { + free (dynamicFilterBuf); + dynamicFilterBuf = NULL; + } + + /* If no entry was returned, try the next search descriptor. */ + if (sd != NULL && sd->lsd_next != NULL) + { + if (stat == NSS_STATUS_NOTFOUND || + (stat == NSS_STATUS_SUCCESS && + ldap_first_entry (__session.ls_conn, *res) == NULL)) + { + sd = sd->lsd_next; + goto next; + } + } + + debug ("<== _nss_ldap_search_s"); + + return stat; +} + +/* + * The generic lookup cover function (asynchronous). + * Assumes caller holds lock. + */ +static enum nss_status +_nss_ldap_search (const struct ldap_args * args, + const char *filterprot, enum ldap_map_selector sel, + const char **user_attrs, int sizelimit, int *msgid, + struct ldap_service_search_descriptor ** csd) +{ + char sdBase[LDAP_FILT_MAXSIZ]; + const char *base = NULL; + char filterBuf[LDAP_FILT_MAXSIZ], *dynamicFilterBuf = NULL; + const char **attrs, *filter; + int scope; + enum nss_status stat; + struct ldap_service_search_descriptor *sd = NULL; + + debug ("==> _nss_ldap_search"); + + *msgid = -1; + + stat = do_init (); + if (stat != NSS_STATUS_SUCCESS) + { + debug ("<== _nss_ldap_search"); + return stat; + } + + /* Set some reasonable defaults. */ + base = __session.ls_config->ldc_base; + scope = __session.ls_config->ldc_scope; + attrs = NULL; + + if (args != NULL && args->la_base != NULL) + { + sel = LM_NONE; + base = args->la_base; + } + + if (sel < LM_NONE || *csd != NULL) + { + /* + * If we were chasing multiple descriptors and there are none left, + * just quit with NSS_STATUS_NOTFOUND. + */ + if (*csd != NULL) + { + sd = (*csd)->lsd_next; + if (sd == NULL) + return NSS_STATUS_NOTFOUND; + } + else + { + sd = __session.ls_config->ldc_sds[sel]; + } + + *csd = sd; + + if (sd != NULL) + { + size_t len = strlen (sd->lsd_base); + if (sd->lsd_base[len - 1] == ',') + { + /* is relative */ + snprintf (sdBase, sizeof (sdBase), "%s%s", sd->lsd_base, + __session.ls_config->ldc_base); + base = sdBase; + } + else + { + base = sd->lsd_base; + } + + if (sd->lsd_scope != -1) + { + scope = sd->lsd_scope; + } + } + attrs = __session.ls_config->ldc_attrtab[sel]; + } + + stat = + do_filter (args, filterprot, sd, filterBuf, sizeof (filterBuf), + &dynamicFilterBuf, &filter); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = do_with_reconnect (base, scope, filter, + (user_attrs != NULL) ? user_attrs : attrs, + sizelimit, msgid, (search_func_t) do_search); + + if (dynamicFilterBuf != NULL) + free (dynamicFilterBuf); + + debug ("<== _nss_ldap_search"); + + return stat; +} + +#ifdef HAVE_LDAP_SEARCH_EXT +static enum nss_status +do_next_page (const struct ldap_args * args, + const char *filterprot, enum ldap_map_selector sel, int + sizelimit, int *msgid, struct berval *pCookie) +{ + char sdBase[LDAP_FILT_MAXSIZ]; + const char *base = NULL; + char filterBuf[LDAP_FILT_MAXSIZ], *dynamicFilterBuf = NULL; + const char **attrs, *filter; + int scope; + enum nss_status stat; + struct ldap_service_search_descriptor *sd = NULL; + LDAPControl *serverctrls[2] = { + NULL, NULL + }; + + /* Set some reasonable defaults. */ + base = __session.ls_config->ldc_base; + scope = __session.ls_config->ldc_scope; + attrs = NULL; + + if (args != NULL && args->la_base != NULL) + { + sel = LM_NONE; + base = args->la_base; + } + + if (sel < LM_NONE) + { + sd = __session.ls_config->ldc_sds[sel]; + if (sd != NULL) + { + size_t len = strlen (sd->lsd_base); + if (sd->lsd_base[len - 1] == ',') + { + snprintf (sdBase, sizeof (sdBase), "%s%s", sd->lsd_base, + __session.ls_config->ldc_base); + base = sdBase; + } + else + { + base = sd->lsd_base; + } + + if (sd->lsd_scope != -1) + { + scope = sd->lsd_scope; + } + } + attrs = __session.ls_config->ldc_attrtab[sel]; + } + + stat = + do_filter (args, filterprot, sd, filterBuf, sizeof (filterBuf), + &dynamicFilterBuf, &filter); + if (stat != NSS_STATUS_SUCCESS) + { + return stat; + } + + stat = + ldap_create_page_control (__session.ls_conn, + __session.ls_config->ldc_pagesize, + pCookie, 0, &serverctrls[0]); + if (stat != LDAP_SUCCESS) + { + if (dynamicFilterBuf != NULL) + free (dynamicFilterBuf); + return NSS_STATUS_UNAVAIL; + } + + stat = + ldap_search_ext (__session.ls_conn, base, + __session.ls_config->ldc_scope, + filter, + (char **) attrs, 0, serverctrls, NULL, LDAP_NO_LIMIT, + sizelimit, msgid); + + ldap_control_free (serverctrls[0]); + if (dynamicFilterBuf != NULL) + free (dynamicFilterBuf); + + return (*msgid < 0) ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; +} +#endif /* HAVE_LDAP_SEARCH_EXT */ + +/* + * General entry point for enumeration routines. + * This should really use the asynchronous LDAP search API to avoid + * pulling down all the entries at once, particularly if the + * enumeration is not completed. + * Locks mutex. + */ +enum nss_status +_nss_ldap_getent (struct ent_context ** ctx, + void *result, char *buffer, size_t buflen, + int *errnop, const char *filterprot, + enum ldap_map_selector sel, parser_t parser) +{ + enum nss_status status; + + /* + * we need to lock here as the context may not be thread-specific + * data (under glibc, for example). Maybe we should make the lock part + * of the context. + */ + + _nss_ldap_enter (); + status = _nss_ldap_getent_ex (NULL, ctx, result, + buffer, buflen, + errnop, filterprot, sel, NULL, parser); + _nss_ldap_leave (); + + return status; +} + +/* + * Internal entry point for enumeration routines. + * Caller holds global mutex + */ +enum nss_status +_nss_ldap_getent_ex (struct ldap_args * args, + struct ent_context ** ctx, void *result, + char *buffer, size_t buflen, int *errnop, + const char *filterprot, + enum ldap_map_selector sel, + const char **user_attrs, parser_t parser) +{ + enum nss_status stat = NSS_STATUS_SUCCESS; + + debug ("==> _nss_ldap_getent_ex"); + + if (*ctx == NULL || (*ctx)->ec_msgid < 0) + { + /* + * implicitly call setent() if this is the first time + * or there is no active search + */ + if (_nss_ldap_ent_context_init_locked (ctx) == NULL) + { + debug ("<== _nss_ldap_getent_ex"); + return NSS_STATUS_UNAVAIL; + } + } + +next: + /* + * If ctx->ec_msgid < 0, then we haven't searched yet. Let's do it! + */ + if ((*ctx)->ec_msgid < 0) + { + int msgid; + + stat = _nss_ldap_search (args, filterprot, sel, user_attrs, + LDAP_NO_LIMIT, &msgid, &(*ctx)->ec_sd); + if (stat != NSS_STATUS_SUCCESS) + { + debug ("<== _nss_ldap_getent_ex"); + return stat; + } + + (*ctx)->ec_msgid = msgid; + } + + stat = do_parse (*ctx, result, buffer, buflen, errnop, parser); + +#ifdef HAVE_LDAP_SEARCH_EXT + if (stat == NSS_STATUS_NOTFOUND) + { + /* Is there another page of results? */ + if ((*ctx)->ec_cookie != NULL && (*ctx)->ec_cookie->bv_len != 0) + { + int msgid; + + stat = + do_next_page (NULL, filterprot, sel, LDAP_NO_LIMIT, &msgid, + (*ctx)->ec_cookie); + if (stat != NSS_STATUS_SUCCESS) + { + debug ("<== _nss_ldap_getent_ex"); + return stat; + } + (*ctx)->ec_msgid = msgid; + stat = do_parse (*ctx, result, buffer, buflen, errnop, parser); + } + } +#endif /* HAVE_LDAP_SEARCH_EXT */ + + if (stat == NSS_STATUS_NOTFOUND && (*ctx)->ec_sd != NULL) + { + (*ctx)->ec_msgid = -1; + goto next; + } + + debug ("<== _nss_ldap_getent_ex"); + + return stat; +} + +/* + * General match function. + * Locks mutex. + */ +enum nss_status +_nss_ldap_getbyname (struct ldap_args * args, + void *result, char *buffer, size_t buflen, int + *errnop, const char *filterprot, + enum ldap_map_selector sel, parser_t parser) +{ + enum nss_status stat = NSS_STATUS_NOTFOUND; + struct ent_context ctx; + + _nss_ldap_enter (); + + debug ("==> _nss_ldap_getbyname"); + + ctx.ec_msgid = -1; + ctx.ec_cookie = NULL; + + stat = _nss_ldap_search_s (args, filterprot, sel, NULL, 1, &ctx.ec_res); + if (stat != NSS_STATUS_SUCCESS) + { + _nss_ldap_leave (); + debug ("<== _nss_ldap_getbyname"); + return stat; + } + + /* + * we pass this along for the benefit of the services parser, + * which uses it to figure out which protocol we really wanted. + * we only pass the second argument along, as that's what we need + * in services. + */ + LS_INIT (ctx.ec_state); + ctx.ec_state.ls_type = LS_TYPE_KEY; + ctx.ec_state.ls_info.ls_key = args->la_arg2.la_string; + + stat = do_parse_s (&ctx, result, buffer, buflen, errnop, parser); + + _nss_ldap_ent_context_release (&ctx); + + debug ("<== _nss_ldap_getbyname"); + + /* moved unlock here to avoid race condition bug #49 */ + _nss_ldap_leave (); + + return stat; +} + +static int NEW_do_parse_s(struct ent_context *ctx,FILE *fp,NEWparser_t parser) +{ + int parseStat=NSLCD_RESULT_NOTFOUND; + LDAPMessage *e=NULL; + /* + * if ec_state.ls_info.ls_index is non-zero, then we don't collect another + * entry off the LDAP chain, and instead refeed the existing result to + * the parser. Once the parser has finished with it, it will return + * NSS_STATUS_NOTFOUND and reset the index to -1, at which point we'll retrieve + * another entry. + */ + do + { + if (ctx->ec_state.ls_retry == 0 && + (ctx->ec_state.ls_type == LS_TYPE_KEY + || ctx->ec_state.ls_info.ls_index == -1)) + { + if (e == NULL) + e = ldap_first_entry (__session.ls_conn, ctx->ec_res); + else + e = ldap_next_entry (__session.ls_conn, e); + } + if (e == NULL) + { + /* Could not get a result; bail */ + parseStat=NSLCD_RESULT_NOTFOUND; + break; + } + /* + * We have an entry; now, try to parse it. + * + * If we do not parse the entry because of a schema + * violation, the parser should return NSS_STATUS_NOTFOUND. + * We'll keep on trying subsequent entries until we + * find one which is parseable, or exhaust avialable + * entries, whichever is first. + */ + parseStat=parser(e,&ctx->ec_state,fp); + /* hold onto the state if we're out of memory XXX */ + ctx->ec_state.ls_retry=0; + } + while (parseStat==NSLCD_RESULT_NOTFOUND); + return parseStat; +} + + +int _nss_ldap_searchbyname( + struct ldap_args *args,const char *filterprot, + enum ldap_map_selector sel,FILE *fp,NEWparser_t parser) +{ + int stat; + struct ent_context ctx; + int32_t tmpint32; + + _nss_ldap_enter(); + + ctx.ec_msgid=-1; + ctx.ec_cookie=NULL; + + stat=nss2nslcd(_nss_ldap_search_s(args,filterprot,sel,NULL,1,&ctx.ec_res)); + /* write the result code */ + WRITE_INT32(fp,stat); + /* bail on nothing found */ + if (stat!=NSLCD_RESULT_SUCCESS) + { + _nss_ldap_leave(); + return 1; + } + /* + * we pass this along for the benefit of the services parser, + * which uses it to figure out which protocol we really wanted. + * we only pass the second argument along, as that's what we need + * in services. + */ + LS_INIT(ctx.ec_state); + ctx.ec_state.ls_type=LS_TYPE_KEY; + ctx.ec_state.ls_info.ls_key=args->la_arg2.la_string; + /* call the parser for the result */ + stat=NEW_do_parse_s(&ctx,fp,parser); + + _nss_ldap_ent_context_release(&ctx); + + /* moved unlock here to avoid race condition bug #49 */ + _nss_ldap_leave(); + + return stat; +} + +/* + * These functions are called from within the parser, where it is assumed + * to be safe to use the connection and the respective message. + */ + +/* + * Assign all values, bar omitvalue (if not NULL), to *valptr. + */ +enum nss_status +_nss_ldap_assign_attrvals (LDAPMessage * e, + const char *attr, const char *omitvalue, + char ***valptr, char **pbuffer, size_t * + pbuflen, size_t * pvalcount) +{ + char **vals; + char **valiter; + int valcount; + char **p = NULL; + + register int buflen = *pbuflen; + register char *buffer = *pbuffer; + + if (pvalcount != NULL) + { + *pvalcount = 0; + } + + if (__session.ls_conn == NULL) + { + return NSS_STATUS_UNAVAIL; + } + + vals = ldap_get_values (__session.ls_conn, e, (char *) attr); + + valcount = (vals == NULL) ? 0 : ldap_count_values (vals); + if (bytesleft (buffer, buflen, char *) < (valcount + 1) * sizeof (char *)) + { + ldap_value_free (vals); + return NSS_STATUS_TRYAGAIN; + } + + align (buffer, buflen, char *); + p = *valptr = (char **) buffer; + + buffer += (valcount + 1) * sizeof (char *); + buflen -= (valcount + 1) * sizeof (char *); + + if (valcount == 0) + { + *p = NULL; + *pbuffer = buffer; + *pbuflen = buflen; + return NSS_STATUS_SUCCESS; + } + + valiter = vals; + + while (*valiter != NULL) + { + int vallen; + char *elt = NULL; + + if (omitvalue != NULL && strcmp (*valiter, omitvalue) == 0) + { + valcount--; + } + else + { + vallen = strlen (*valiter); + if (buflen < (size_t) (vallen + 1)) + { + ldap_value_free (vals); + return NSS_STATUS_TRYAGAIN; + } + + /* copy this value into the next block of buffer space */ + elt = buffer; + buffer += vallen + 1; + buflen -= vallen + 1; + + strncpy (elt, *valiter, vallen); + elt[vallen] = '\0'; + *p = elt; + p++; + } + valiter++; + } + + *p = NULL; + *pbuffer = buffer; + *pbuflen = buflen; + + if (pvalcount != NULL) + { + *pvalcount = valcount; + } + + ldap_value_free (vals); + return NSS_STATUS_SUCCESS; +} + +int _nss_ldap_write_attrvals(FILE *fp,LDAPMessage *e,const char *attr) +{ + char **vals; + int valcount; + int i; + int32_t tmpint32; + /* log */ + log_log(LOG_DEBUG,"_nss_ldap_write_attrvals(%s)",attr); + /* check if we have a connection */ + if (__session.ls_conn==NULL) + return NSLCD_RESULT_UNAVAIL; + /* get the values and the number of values */ + vals=ldap_get_values(__session.ls_conn,e,(char *)attr); + valcount=(vals==NULL)?0:ldap_count_values(vals); + /* write number of entries */ + WRITE_INT32(fp,valcount); + /* write the entries themselves */ + for (i=0;i<valcount;i++) + { + WRITE_STRING(fp,vals[i]); + } + if (vals!=NULL) + ldap_value_free(vals); + return NSLCD_RESULT_SUCCESS; +} + +/* Assign a single value to *valptr. */ +enum nss_status +_nss_ldap_assign_attrval (LDAPMessage * e, + const char *attr, char **valptr, char **buffer, + size_t * buflen) +{ + char **vals; + int vallen; + const char *ovr, *def; + + ovr = OV (attr); + if (ovr != NULL) + { + vallen = strlen (ovr); + if (*buflen < (size_t) (vallen + 1)) + { + return NSS_STATUS_TRYAGAIN; + } + + *valptr = *buffer; + + strncpy (*valptr, ovr, vallen); + (*valptr)[vallen] = '\0'; + + *buffer += vallen + 1; + *buflen -= vallen + 1; + + return NSS_STATUS_SUCCESS; + } + + if (__session.ls_conn == NULL) + { + return NSS_STATUS_UNAVAIL; + } + + vals = ldap_get_values (__session.ls_conn, e, (char *) attr); + if (vals == NULL) + { + def = DF (attr); + if (def != NULL) + { + vallen = strlen (def); + if (*buflen < (size_t) (vallen + 1)) + { + return NSS_STATUS_TRYAGAIN; + } + + *valptr = *buffer; + + strncpy (*valptr, def, vallen); + (*valptr)[vallen] = '\0'; + + *buffer += vallen + 1; + *buflen -= vallen + 1; + + return NSS_STATUS_SUCCESS; + } + else + { + return NSS_STATUS_NOTFOUND; + } + } + + vallen = strlen (*vals); + if (*buflen < (size_t) (vallen + 1)) + { + ldap_value_free (vals); + return NSS_STATUS_TRYAGAIN; + } + + *valptr = *buffer; + + strncpy (*valptr, *vals, vallen); + (*valptr)[vallen] = '\0'; + + *buffer += vallen + 1; + *buflen -= vallen + 1; + + ldap_value_free (vals); + + return NSS_STATUS_SUCCESS; +} + +const char * +_nss_ldap_locate_userpassword (char **vals) +{ + const char *token = NULL; + size_t token_length = 0; + char **valiter; + const char *pwd = NULL; + + if (__config != NULL) + { + switch (__config->ldc_password_type) + { + case LU_RFC2307_USERPASSWORD: + token = "{CRYPT}"; + token_length = sizeof ("{CRYPT}") - 1; + break; + case LU_RFC3112_AUTHPASSWORD: + token = "CRYPT$"; + token_length = sizeof ("CRYPT$") - 1; + break; + case LU_OTHER_PASSWORD: + break; + } + } + + if (vals != NULL) + { + for (valiter = vals; *valiter != NULL; valiter++) + { + if (token_length == 0 || + strncasecmp (*valiter, token, token_length) == 0) + { + pwd = *valiter; + break; + } + } + } + + if (pwd == NULL) + pwd = "*"; + else + pwd += token_length; + + return pwd; +} + +/* + * Assign a single value to *valptr, after examining userPassword for + * a syntactically suitable value. + */ +enum nss_status +_nss_ldap_assign_userpassword (LDAPMessage * e, + const char *attr, char **valptr, + char **buffer, size_t * buflen) +{ + char **vals; + const char *pwd; + int vallen; + + debug ("==> _nss_ldap_assign_userpassword"); + + if (__session.ls_conn == NULL) + { + return NSS_STATUS_UNAVAIL; + } + + vals = ldap_get_values (__session.ls_conn, e, (char *) attr); + pwd = _nss_ldap_locate_userpassword (vals); + + vallen = strlen (pwd); + + if (*buflen < (size_t) (vallen + 1)) + { + if (vals != NULL) + { + ldap_value_free (vals); + } + debug ("<== _nss_ldap_assign_userpassword"); + return NSS_STATUS_TRYAGAIN; + } + + *valptr = *buffer; + + strncpy (*valptr, pwd, vallen); + (*valptr)[vallen] = '\0'; + + *buffer += vallen + 1; + *buflen -= vallen + 1; + + if (vals != NULL) + { + ldap_value_free (vals); + } + + debug ("<== _nss_ldap_assign_userpassword"); + + return NSS_STATUS_SUCCESS; +} + +enum nss_status +_nss_ldap_oc_check (LDAPMessage * e, const char *oc) +{ + char **vals, **valiter; + enum nss_status ret = NSS_STATUS_NOTFOUND; + + if (__session.ls_conn == NULL) + { + return NSS_STATUS_UNAVAIL; + } + + vals = ldap_get_values (__session.ls_conn, e, AT (objectClass)); + if (vals != NULL) + { + for (valiter = vals; *valiter != NULL; valiter++) + { + if (strcasecmp (*valiter, oc) == 0) + { + ret = NSS_STATUS_SUCCESS; + break; + } + } + } + + if (vals != NULL) + { + ldap_value_free (vals); + } + + return ret; +} + +#ifdef HAVE_SHADOW_H +int +_nss_ldap_shadow_date (const char *val) +{ + int date; + + if (__config->ldc_shadow_type == LS_AD_SHADOW) + { + date = atoll (val) / 864000000000LL - 134774LL; + date = (date > 99999) ? 99999 : date; + } + else + { + date = atol (val); + } + + return date; +} + +void +_nss_ldap_shadow_handle_flag (struct spwd *sp) +{ + if (__config->ldc_shadow_type == LS_AD_SHADOW) + { + if (sp->sp_flag & UF_DONT_EXPIRE_PASSWD) + sp->sp_max = 99999; + sp->sp_flag = 0; + } +} +#endif /* HAVE_SHADOW_H */ + +const char * +_nss_ldap_map_at (enum ldap_map_selector sel, const char *attribute) +{ + const char *mapped = NULL; + enum nss_status stat; + + stat = _nss_ldap_map_get (__config, sel, MAP_ATTRIBUTE, attribute, &mapped); + + return (stat == NSS_STATUS_SUCCESS) ? mapped : attribute; +} + +const char * +_nss_ldap_unmap_at (enum ldap_map_selector sel, const char *attribute) +{ + const char *mapped = NULL; + enum nss_status stat; + + stat = _nss_ldap_map_get (__config, sel, MAP_ATTRIBUTE_REVERSE, attribute, &mapped); + + return (stat == NSS_STATUS_SUCCESS) ? mapped : attribute; +} + +const char * +_nss_ldap_map_oc (enum ldap_map_selector sel, const char *objectclass) +{ + const char *mapped = NULL; + enum nss_status stat; + + stat = _nss_ldap_map_get (__config, sel, MAP_OBJECTCLASS, objectclass, &mapped); + + return (stat == NSS_STATUS_SUCCESS) ? mapped : objectclass; +} + +const char * +_nss_ldap_unmap_oc (enum ldap_map_selector sel, const char *objectclass) +{ + const char *mapped = NULL; + enum nss_status stat; + + stat = _nss_ldap_map_get (__config, sel, MAP_OBJECTCLASS_REVERSE, objectclass, &mapped); + + return (stat == NSS_STATUS_SUCCESS) ? mapped : objectclass; +} + +const char * +_nss_ldap_map_ov (const char *attribute) +{ + const char *value = NULL; + + _nss_ldap_map_get (__config, LM_NONE, MAP_OVERRIDE, attribute, &value); + + return value; +} + +const char * +_nss_ldap_map_df (const char *attribute) +{ + const char *value = NULL; + + _nss_ldap_map_get (__config, LM_NONE, MAP_DEFAULT, attribute, &value); + + return value; +} + +enum nss_status +_nss_ldap_map_put (struct ldap_config * config, + enum ldap_map_selector sel, + enum ldap_map_type type, + const char *from, + const char *to) +{ + struct ldap_datum key, val; + void **map; + enum nss_status stat; + + switch (type) + { + case MAP_ATTRIBUTE: + /* special handling for attribute mapping */ if (strcmp + (from, + "userPassword") == 0) + { + if (strcasecmp (to, "userPassword") == 0) + config->ldc_password_type = LU_RFC2307_USERPASSWORD; + else if (strcasecmp (to, "authPassword") == 0) + config->ldc_password_type = LU_RFC3112_AUTHPASSWORD; + else + config->ldc_password_type = LU_OTHER_PASSWORD; + } + else if (strcmp (from, "shadowLastChange") == 0) + { + if (strcasecmp (to, "shadowLastChange") == 0) + config->ldc_shadow_type = LS_RFC2307_SHADOW; + else if (strcasecmp (to, "pwdLastSet") == 0) + config->ldc_shadow_type = LS_AD_SHADOW; + else + config->ldc_shadow_type = LS_OTHER_SHADOW; + } + break; + case MAP_OBJECTCLASS: + case MAP_OVERRIDE: + case MAP_DEFAULT: + break; + default: + return NSS_STATUS_NOTFOUND; + break; + } + + assert (sel <= LM_NONE); + map = &config->ldc_maps[sel][type]; + assert (*map != NULL); + + NSS_LDAP_DATUM_ZERO (&key); + key.data = (void *) from; + key.size = strlen (from) + 1; + + NSS_LDAP_DATUM_ZERO (&val); + val.data = (void *) to; + val.size = strlen (to) + 1; + + stat = _nss_ldap_db_put (*map, NSS_LDAP_DB_NORMALIZE_CASE, &key, &val); + if (stat == NSS_STATUS_SUCCESS && + (type == MAP_ATTRIBUTE || type == MAP_OBJECTCLASS)) + { + type = (type == MAP_ATTRIBUTE) ? MAP_ATTRIBUTE_REVERSE : MAP_OBJECTCLASS_REVERSE; + map = &config->ldc_maps[sel][type]; + + stat = _nss_ldap_db_put (*map, NSS_LDAP_DB_NORMALIZE_CASE, &val, &key); + } + + return stat; +} + +enum nss_status +_nss_ldap_map_get (struct ldap_config * config, + enum ldap_map_selector sel, + enum ldap_map_type type, + const char *from, const char **to) +{ + struct ldap_datum key, val; + void *map; + enum nss_status stat; + + if (config == NULL || sel > LM_NONE || type > MAP_MAX) + { + return NSS_STATUS_NOTFOUND; + } + + map = config->ldc_maps[sel][type]; + assert (map != NULL); + + NSS_LDAP_DATUM_ZERO (&key); + key.data = from; + key.size = strlen (from) + 1; + + NSS_LDAP_DATUM_ZERO (&val); + + stat = _nss_ldap_db_get (map, NSS_LDAP_DB_NORMALIZE_CASE, &key, &val); + if (stat == NSS_STATUS_NOTFOUND && sel != LM_NONE) + { + map = config->ldc_maps[LM_NONE][type]; + assert (map != NULL); + stat = _nss_ldap_db_get (map, NSS_LDAP_DB_NORMALIZE_CASE, &key, &val); + } + + if (stat == NSS_STATUS_SUCCESS) + *to = (char *) val.data; + else + *to = NULL; + + return stat; +} + +/* + * Proxy bind support for AIX. Very simple, but should do + * the job. + */ + +struct ldap_proxy_bind_args +{ + char *binddn; + const char *bindpw; +}; + + +#if LDAP_SET_REBIND_PROC_ARGS < 3 +static struct ldap_proxy_bind_args __proxy_args = { NULL, NULL }; +#endif + +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) +#if LDAP_SET_REBIND_PROC_ARGS == 3 +static int +do_proxy_rebind (LDAP * ld, LDAP_CONST char *url, ber_tag_t request, + ber_int_t msgid, void *arg) +#else +static int +do_proxy_rebind (LDAP * ld, LDAP_CONST char *url, int request, + ber_int_t msgid) +#endif +{ + int timelimit; +#if LDAP_SET_REBIND_PROC_ARGS == 3 + struct ldap_proxy_bind_args *who = (struct ldap_proxy_bind_args *) arg; +#else + struct ldap_proxy_bind_args *who = &__proxy_args; +#endif + + timelimit = __session.ls_config->ldc_bind_timelimit; + + return do_bind (ld, timelimit, who->binddn, who->bindpw, 0); +} +#else +#if LDAP_SET_REBIND_PROC_ARGS == 3 +static int +do_proxy_rebind (LDAP * ld, char **whop, char **credp, int *methodp, + int freeit, void *arg) +#elif LDAP_SET_REBIND_PROC_ARGS == 2 +static int +do_proxy_rebind (LDAP * ld, char **whop, char **credp, int *methodp, + int freeit) +#endif +{ +#if LDAP_SET_REBIND_PROC_ARGS == 3 + struct ldap_proxy_bind_args *who = (struct ldap_proxy_bind_args *) arg; +#else + struct ldap_proxy_bind_args *who = &__proxy_args; +#endif + if (freeit) + { + if (*whop != NULL) + free (*whop); + if (*credp != NULL) + free (*credp); + } + + *whop = who->binddn ? strdup (who->binddn) : NULL; + *credp = who->bindpw ? strdup (who->bindpw) : NULL; + + *methodp = LDAP_AUTH_SIMPLE; + + return LDAP_SUCCESS; +} +#endif + +enum nss_status +_nss_ldap_proxy_bind (const char *user, const char *password) +{ + struct ldap_args args; + LDAPMessage *res, *e; + enum nss_status stat; + int rc; +#if LDAP_SET_REBIND_PROC_ARGS == 3 + struct ldap_proxy_bind_args proxy_args_buf; + struct ldap_proxy_bind_args *proxy_args = &proxy_args_buf; +#else + struct ldap_proxy_bind_args *proxy_args = &__proxy_args; +#endif + + debug ("==> _nss_ldap_proxy_bind"); + + LA_INIT (args); + LA_TYPE (args) = LA_TYPE_STRING; + LA_STRING (args) = user; + + /* + * Binding with an empty password will always work, so don't let + * the user in if they try that. + */ + if (password == NULL || password[0] == '\0') + { + debug ("<== _nss_ldap_proxy_bind (empty password not permitted)"); + /* XXX overload */ + return NSS_STATUS_TRYAGAIN; + } + + _nss_ldap_enter (); + + stat = _nss_ldap_search_s (&args, _nss_ldap_filt_getpwnam, + LM_PASSWD, NULL, 1, &res); + if (stat == NSS_STATUS_SUCCESS) + { + e = _nss_ldap_first_entry (res); + if (e != NULL) + { + proxy_args->binddn = _nss_ldap_get_dn (e); + proxy_args->bindpw = password; + + if (proxy_args->binddn != NULL) + { + /* Use our special rebind procedure. */ +#if LDAP_SET_REBIND_PROC_ARGS == 3 + ldap_set_rebind_proc (__session.ls_conn, do_proxy_rebind, NULL); +#elif LDAP_SET_REBIND_PROC_ARGS == 2 + ldap_set_rebind_proc (__session.ls_conn, do_proxy_rebind); +#endif + + debug (":== _nss_ldap_proxy_bind: %s", proxy_args->binddn); + + rc = do_bind (__session.ls_conn, + __session.ls_config->ldc_bind_timelimit, + proxy_args->binddn, proxy_args->bindpw, 0); + switch (rc) + { + case LDAP_INVALID_CREDENTIALS: + /* XXX overload */ + stat = NSS_STATUS_TRYAGAIN; + break; + case LDAP_NO_SUCH_OBJECT: + stat = NSS_STATUS_NOTFOUND; + break; + case LDAP_SUCCESS: + stat = NSS_STATUS_SUCCESS; + break; + default: + stat = NSS_STATUS_UNAVAIL; + break; + } + /* + * Close the connection, don't want to continue + * being bound as this user or using this rebind proc. + */ + do_close (); + ldap_memfree (proxy_args->binddn); + } + else + { + stat = NSS_STATUS_NOTFOUND; + } + proxy_args->binddn = NULL; + proxy_args->bindpw = NULL; + } + else + { + stat = NSS_STATUS_NOTFOUND; + } + ldap_msgfree (res); + } + + _nss_ldap_leave (); + + debug ("<== _nss_ldap_proxy_bind"); + + return stat; +} + +const char ** +_nss_ldap_get_attributes (enum ldap_map_selector sel) +{ + const char **attrs = NULL; + + debug ("==> _nss_ldap_get_attributes"); + + if (sel < LM_NONE) + { + if (do_init () != NSS_STATUS_SUCCESS) + { + debug ("<== _nss_ldap_get_attributes (init failed)"); + return NULL; + } + + attrs = __session.ls_config->ldc_attrtab[sel]; + } + + debug ("<== _nss_ldap_get_attributes"); + + return attrs; +} + +int +_nss_ldap_test_config_flag (unsigned int flag) +{ + if (__config != NULL && (__config->ldc_flags & flag) != 0) + return 1; + + return 0; +} + +int +_nss_ldap_test_initgroups_ignoreuser (const char *user) +{ + char **p; + + if (__config == NULL) + return 0; + + if (__config->ldc_initgroups_ignoreusers == NULL) + return 0; + + for (p = __config->ldc_initgroups_ignoreusers; *p != NULL; p++) + { + if (strcmp (*p, user) == 0) + return 1; + } + + return 0; +} diff --git a/nslcd/ldap-nss.h b/nslcd/ldap-nss.h new file mode 100644 index 0000000..b59d774 --- /dev/null +++ b/nslcd/ldap-nss.h @@ -0,0 +1,611 @@ +/* + ldap-nss.c - main file for NSS interface + This file was part of the nss_ldap library which has been + forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#ifndef _LDAP_NSS_LDAP_LDAP_NSS_H +#define _LDAP_NSS_LDAP_LDAP_NSS_H + +/* for glibc, use weak aliases to pthreads functions */ +#ifdef HAVE_LIBC_LOCK_H +#include <libc-lock.h> +#elif defined(HAVE_BITS_LIBC_LOCK_H) +#include <bits/libc-lock.h> +#endif + +#include <time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#ifdef HAVE_SHADOW_H +#include <shadow.h> +#endif + +#include <netdb.h> +#include <netinet/in.h> +#include <syslog.h> + +#include <nss.h> + +#include "ldap-schema.h" + +#ifndef NSS_BUFSIZ +#define NSS_BUFSIZ 1024 +#endif + +#ifndef NSS_BUFLEN_GROUP +#define NSS_BUFLEN_GROUP LDAP_NSS_BUFLEN_GROUP +#endif + +#ifndef LDAP_FILT_MAXSIZ +#define LDAP_FILT_MAXSIZ 1024 +#endif /* not LDAP_FILT_MAXSIZ */ + +#ifndef LDAPS_PORT +#define LDAPS_PORT 636 +#endif /* not LDAPS_PORT */ + +#ifdef DEBUG +#ifdef __XGNUC__ +#define debug(fmt, args...) fprintf(stderr, "nss_ldap: " fmt "\n" , ## args) +#else +#include <stdarg.h> +#include <stdio.h> +static void +debug (char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + fprintf (stderr, "nss_ldap: "); + vfprintf (stderr, fmt, ap); + va_end (ap); + fprintf (stderr, "\n"); +} +#endif /* __GNUC__ */ +#else /* DEBUG */ +#ifdef __GNUC__ +#define debug(fmt, args...) +#else /* __GNUC__ */ +static void +debug (char *fmt, ...) +{ +} +#endif /* not __GNUC__ */ +#endif /* not DEBUG */ + +#ifdef __GNUC__ +#define alignof(ptr) __alignof__(ptr) +#elif defined(HAVE_ALIGNOF_H) +#include <alignof.h> +#else +#define alignof(ptr) (sizeof(char *)) +#endif /* __GNUC__ */ + +#define align(ptr, blen, TYPE)\ + { \ + char *qtr = ptr; \ + ptr += alignof(TYPE) - 1; \ + ptr -= ((ptr - (char *)NULL) % alignof(TYPE)); \ + blen -= (ptr - qtr); \ + } + +/* worst case */ +#define bytesleft(ptr, blen, TYPE) \ + ( (blen < alignof(TYPE)) ? 0 : (blen - alignof(TYPE) + 1)) + +/* selectors for different maps */ +enum ldap_map_selector +{ + LM_PASSWD, + LM_SHADOW, + LM_GROUP, + LM_HOSTS, + LM_SERVICES, + LM_NETWORKS, + LM_PROTOCOLS, + LM_RPC, + LM_ETHERS, + LM_NETMASKS, + LM_BOOTPARAMS, + LM_ALIASES, + LM_NETGROUP, + LM_NONE +}; + +enum ldap_userpassword_selector +{ + LU_RFC2307_USERPASSWORD, + LU_RFC3112_AUTHPASSWORD, + LU_OTHER_PASSWORD +}; + +enum ldap_shadow_selector +{ + LS_RFC2307_SHADOW, + LS_AD_SHADOW, + LS_OTHER_SHADOW +}; + +#ifndef UF_DONT_EXPIRE_PASSWD +#define UF_DONT_EXPIRE_PASSWD 0x10000 +#endif + +enum ldap_ssl_options +{ + SSL_OFF, + SSL_LDAPS, + SSL_START_TLS +}; + +enum ldap_reconnect_policy +{ + LP_RECONNECT_HARD_INIT, + LP_RECONNECT_HARD_OPEN, + LP_RECONNECT_SOFT +}; + +/* + * POSIX profile information (not used yet) + * see draft-joslin-config-schema-00.txt + */ +struct ldap_service_search_descriptor +{ + /* search base, qualified */ + char *lsd_base; + /* scope */ + int lsd_scope; + /* filter */ + char *lsd_filter; + /* next */ + struct ldap_service_search_descriptor *lsd_next; +}; + +/* maximum number of URIs */ +#define NSS_LDAP_CONFIG_URI_MAX 31 + +/* + * linked list of configurations pointing to LDAP servers. The first + * which has a successful ldap_open() is used. Conceivably the rest + * could be used after a failed or exhausted search. + */ +struct ldap_config +{ + /* NULL terminated list of URIs */ + char *ldc_uris[NSS_LDAP_CONFIG_URI_MAX + 1]; + /* default port, if not specified in URI */ + int ldc_port; + /* base DN, eg. dc=gnu,dc=org */ + char *ldc_base; + /* scope for searches */ + int ldc_scope; + /* dereference aliases/links */ + int ldc_deref; + /* bind DN */ + char *ldc_binddn; + /* bind cred */ + char *ldc_bindpw; + /* sasl auth id */ + char *ldc_saslid; + /* do we use sasl when binding? */ + int ldc_usesasl; + /* shadow bind DN */ + char *ldc_rootbinddn; + /* shadow bind cred */ + char *ldc_rootbindpw; + /* shadow sasl auth id */ + char *ldc_rootsaslid; + /* do we use sasl for root? */ + int ldc_rootusesasl; + /* protocol version */ + int ldc_version; + /* search timelimit */ + int ldc_timelimit; + /* bind timelimit */ + int ldc_bind_timelimit; + /* SSL enabled */ + enum ldap_ssl_options ldc_ssl_on; + /* SSL certificate path */ + char *ldc_sslpath; + /* Chase referrals */ + int ldc_referrals; + int ldc_restart; + /* naming contexts */ + struct ldap_service_search_descriptor *ldc_sds[LM_NONE]; + /* tls check peer */ + int ldc_tls_checkpeer; + /* tls ca certificate file */ + char *ldc_tls_cacertfile; + /* tls ca certificate dir */ + char *ldc_tls_cacertdir; + /* tls ciphersuite */ + char *ldc_tls_ciphers; + /* tls certificate */ + char *ldc_tls_cert; + /* tls key */ + char *ldc_tls_key; + /* tls randfile */ + char *ldc_tls_randfile; + /* idle timeout */ + time_t ldc_idle_timelimit; + /* reconnect policy */ + enum ldap_reconnect_policy ldc_reconnect_pol; + int ldc_reconnect_tries; + int ldc_reconnect_sleeptime; + int ldc_reconnect_maxsleeptime; + int ldc_reconnect_maxconntries; + /* sasl security */ + char *ldc_sasl_secprops; + /* DNS SRV RR domain */ + char *ldc_srv_domain; + /* directory for debug files */ + char *ldc_logdir; + /* LDAP debug level */ + int ldc_debug; + int ldc_pagesize; +#ifdef CONFIGURE_KRB5_CCNAME + /* krb5 ccache name */ + char *ldc_krb5_ccname; +#endif /* CONFIGURE_KRB5_CCNAME */ + /* attribute/objectclass maps relative to this config */ + void *ldc_maps[LM_NONE + 1][6]; /* must match MAP_MAX */ + /* is userPassword "userPassword" or not? ie. do we need {crypt} to be stripped */ + enum ldap_userpassword_selector ldc_password_type; + /* Use active directory time offsets? */ + enum ldap_shadow_selector ldc_shadow_type; + /* attribute table for ldap search requensts */ + const char **ldc_attrtab[LM_NONE + 1]; + unsigned int ldc_flags; + /* last modification time */ + time_t ldc_mtime; + char **ldc_initgroups_ignoreusers; +}; + +#if defined(__GLIBC__) && __GLIBC_MINOR__ > 1 +#else +#define ss_family sa_family +#endif /* __GLIBC__ */ + +enum ldap_session_state +{ + LS_UNINITIALIZED = -1, + LS_INITIALIZED, + LS_CONNECTED_TO_DSA +}; + +/* + * convenient wrapper around pointer into global config list, and a + * connection to an LDAP server. + */ +struct ldap_session +{ + /* the connection */ + LDAP *ls_conn; + /* pointer into config table */ + struct ldap_config *ls_config; + /* timestamp of last activity */ + time_t ls_timestamp; + /* has session been connected? */ + enum ldap_session_state ls_state; + /* keep track of the LDAP sockets */ + struct sockaddr_storage ls_sockname; + struct sockaddr_storage ls_peername; + /* index into ldc_uris: currently connected DSA */ + int ls_current_uri; +}; + +enum ldap_args_types +{ + LA_TYPE_STRING, + LA_TYPE_NUMBER, + LA_TYPE_STRING_AND_STRING, + LA_TYPE_NUMBER_AND_STRING, + LA_TYPE_TRIPLE, + LA_TYPE_STRING_LIST_OR, + LA_TYPE_STRING_LIST_AND, + LA_TYPE_NONE +}; + +enum ldap_map_type +{ + MAP_ATTRIBUTE = 0, + MAP_OBJECTCLASS, + MAP_OVERRIDE, + MAP_DEFAULT, + MAP_ATTRIBUTE_REVERSE, + MAP_OBJECTCLASS_REVERSE, /* XXX not used yet? */ + MAP_MAX = MAP_OBJECTCLASS_REVERSE +}; + +struct ldap_args +{ + enum ldap_args_types la_type; + union + { + const char *la_string; + long la_number; + struct { + /* for Solaris netgroup support */ + const char *host; + const char *user; + const char *domain; + } la_triple; + const char **la_string_list; + } + la_arg1; + union + { + const char *la_string; + } + la_arg2; + const char *la_base; /* override default base */ +}; + +#define LA_INIT(q) do { \ + (q).la_type = LA_TYPE_STRING; \ + (q).la_arg1.la_string = NULL; \ + (q).la_arg2.la_string = NULL; \ + (q).la_base = NULL; \ + } while (0) +#define LA_TYPE(q) ((q).la_type) +#define LA_STRING(q) ((q).la_arg1.la_string) +#define LA_NUMBER(q) ((q).la_arg1.la_number) +#define LA_TRIPLE(q) ((q).la_arg1.la_triple) +#define LA_STRING_LIST(q) ((q).la_arg1.la_string_list) +#define LA_STRING2(q) ((q).la_arg2.la_string) +#define LA_BASE(q) ((q).la_base) + +/* + * the state consists of the desired attribute value or an offset into a list of + * values for the desired attribute. This is necessary to support services. + * + * Be aware of the arbitary distinction between state and context. Context is + * the enumeration state of a lookup subsystem (which may be per-subsystem, + * or per-subsystem/per-thread, depending on the OS). State is the state + * of a particular lookup, and is only concerned with resolving and enumerating + * services. State is represented as instances of struct ldap_state; context as + * instances of struct ent_context. The latter contains the former. + */ +struct ldap_state +{ + int ls_type; + int ls_retry; +#define LS_TYPE_KEY (0) +#define LS_TYPE_INDEX (1) + union + { + /* ls_key is the requested attribute value. + ls_index is the desired offset into the value list. + */ + const char *ls_key; + int ls_index; + } + ls_info; +}; + +/* + * thread specific context: result chain, and state data + */ +struct ent_context +{ + struct ldap_state ec_state; /* eg. for services */ + int ec_msgid; /* message ID */ + LDAPMessage *ec_res; /* result chain */ + struct ldap_service_search_descriptor *ec_sd; /* current sd */ + struct berval *ec_cookie; /* cookie for paged searches */ +}; + +struct name_list +{ + char *name; + struct name_list *next; +}; + +typedef enum nss_status (*parser_t) (LDAPMessage *, struct ldap_state *, void *, + char *, size_t); + +typedef int (*NEWparser_t)(LDAPMessage *e,struct ldap_state *pvt,FILE *fp); + +/* + * Portable locking macro. + */ +#if defined(HAVE_THREAD_H) +#define NSS_LDAP_LOCK(m) mutex_lock(&m) +#define NSS_LDAP_UNLOCK(m) mutex_unlock(&m) +#define NSS_LDAP_DEFINE_LOCK(m) static mutex_t m = DEFAULTMUTEX +#elif defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H) +#define NSS_LDAP_LOCK(m) __libc_lock_lock(m) +#define NSS_LDAP_UNLOCK(m) __libc_lock_unlock(m) +#define NSS_LDAP_DEFINE_LOCK(m) static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER +#elif defined(HAVE_PTHREAD_H) +#define NSS_LDAP_LOCK(m) pthread_mutex_lock(&m) +#define NSS_LDAP_UNLOCK(m) pthread_mutex_unlock(&m) +#define NSS_LDAP_DEFINE_LOCK(m) static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER +#else +#define NSS_LDAP_LOCK(m) +#define NSS_LDAP_UNLOCK(m) +#define NSS_LDAP_DEFINE_LOCK(m) +#endif + +/* + * Acquire global nss_ldap lock and blocks SIGPIPE. + * Generally this should only be done within ldap-nss.c. + */ +void _nss_ldap_enter (void); + +/* + * Release global nss_ldap lock and blocks SIGPIPE. + * Generally this should only be done within ldap-nss.c. + */ +void _nss_ldap_leave (void); + +/* + * _nss_ldap_ent_context_init() is called for each getXXent() call + * This will acquire the global mutex. + */ +struct ent_context *_nss_ldap_ent_context_init (struct ent_context **); + +/* + * _nss_ldap_ent_context_init_locked() has the same behaviour + * as above, except it assumes that the caller has acquired + * the lock + */ + +struct ent_context *_nss_ldap_ent_context_init_locked (struct ent_context **); + +/* + * _nss_ldap_ent_context_release() is used to manually free a context + */ +void _nss_ldap_ent_context_release (struct ent_context *); + +/* + * these are helper functions for ldap-grp.c only on Solaris + */ +char **_nss_ldap_get_values (LDAPMessage * e, const char *attr); +char *_nss_ldap_get_dn (LDAPMessage * e); +LDAPMessage *_nss_ldap_first_entry (LDAPMessage * res); +LDAPMessage *_nss_ldap_next_entry (LDAPMessage * res); +char *_nss_ldap_first_attribute (LDAPMessage * entry, BerElement **berptr); +char *_nss_ldap_next_attribute (LDAPMessage * entry, BerElement *ber); +const char **_nss_ldap_get_attributes (enum ldap_map_selector sel); + +/* + * Synchronous search cover (caller acquires lock). + */ +enum nss_status _nss_ldap_search_s (const struct ldap_args * args, /* IN */ + const char *filterprot, /* IN */ + enum ldap_map_selector sel, /* IN */ + const char **user_attrs, /* IN */ + int sizelimit, /* IN */ + LDAPMessage ** pRes /* OUT */ ); + +/* + * Emulate X.500 read operation. + */ +enum nss_status _nss_ldap_read (const char *dn, /* IN */ + const char **attributes, /* IN */ + LDAPMessage ** pRes /* OUT */ ); + +/* + * extended enumeration routine; uses asynchronous API. + * Caller must have acquired the global mutex + */ +enum nss_status _nss_ldap_getent_ex (struct ldap_args * args, /* IN */ + struct ent_context ** key, /* IN/OUT */ + void *result, /* IN/OUT */ + char *buffer, /* IN */ + size_t buflen, /* IN */ + int *errnop, /* OUT */ + const char *filterprot, /* IN */ + enum ldap_map_selector sel, /* IN */ + const char **user_attrs, /* IN */ + parser_t parser /* IN */ ); + +/* + * common enumeration routine; uses asynchronous API. + * Acquires the global mutex + */ +enum nss_status _nss_ldap_getent (struct ent_context ** key, /* IN/OUT */ + void *result, /* IN/OUT */ + char *buffer, /* IN */ + size_t buflen, /* IN */ + int *errnop, /* OUT */ + const char *filterprot, /* IN */ + enum ldap_map_selector sel, /* IN */ + parser_t parser /* IN */ ); + +/* + * common lookup routine; uses synchronous API. + */ +enum nss_status _nss_ldap_getbyname (struct ldap_args * args, /* IN/OUT */ + void *result, /* IN/OUT */ + char *buffer, /* IN */ + size_t buflen, /* IN */ + int *errnop, /* OUT */ + const char *filterprot, /* IN */ + enum ldap_map_selector sel, /* IN */ + parser_t parser /* IN */ ); + +int _nss_ldap_searchbyname(struct ldap_args *args, /* IN/OUT */ + const char *filterprot, /* IN */ + enum ldap_map_selector sel, /* IN */ + FILE *fp,NEWparser_t parser /* IN */ ); + + +/* parsing utility functions */ +enum nss_status _nss_ldap_assign_attrvals (LDAPMessage * e, /* IN */ + const char *attr, /* IN */ + const char *omitvalue, /* IN */ + char ***valptr, /* OUT */ + char **buffer, /* IN/OUT */ + size_t * buflen, /* IN/OUT */ + size_t * pvalcount /* OUT */ ); + +int _nss_ldap_write_attrvals(FILE *fp,LDAPMessage *e,const char *attr); + +enum nss_status _nss_ldap_assign_attrval (LDAPMessage * e, /* IN */ + const char *attr, /* IN */ + char **valptr, /* OUT */ + char **buffer, /* IN/OUT */ + size_t * buflen /* IN/OUT */ ); + + +const char *_nss_ldap_locate_userpassword (char **vals); + +enum nss_status _nss_ldap_assign_userpassword (LDAPMessage * e, /* IN */ + const char *attr, /* IN */ + char **valptr, /* OUT */ + char **buffer, /* IN/OUT */ + size_t * buflen); /* IN/OUT */ + +enum nss_status _nss_ldap_oc_check (LDAPMessage * e, const char *oc); + +int _nss_ldap_shadow_date(const char *val); +void _nss_ldap_shadow_handle_flag(struct spwd *sp); + +enum nss_status _nss_ldap_map_put (struct ldap_config * config, + enum ldap_map_selector sel, + enum ldap_map_type map, + const char *key, const char *value); + +enum nss_status _nss_ldap_map_get (struct ldap_config * config, + enum ldap_map_selector sel, + enum ldap_map_type map, + const char *key, const char **value); + +const char *_nss_ldap_map_at (enum ldap_map_selector sel, const char *pChar2); +const char *_nss_ldap_unmap_at (enum ldap_map_selector sel, const char *attribute); + +const char *_nss_ldap_map_oc (enum ldap_map_selector sel, const char *pChar); +const char *_nss_ldap_unmap_oc (enum ldap_map_selector sel, const char *pChar); + +const char *_nss_ldap_map_ov (const char *pChar); +const char *_nss_ldap_map_df (const char *pChar); + +enum nss_status _nss_ldap_proxy_bind (const char *user, const char *password); + +enum nss_status _nss_ldap_init (void); +void _nss_ldap_close (void); + +int _nss_ldap_test_config_flag (unsigned int flag); +int _nss_ldap_test_initgroups_ignoreuser (const char *user); + +#endif /* _LDAP_NSS_LDAP_LDAP_NSS_H */ diff --git a/nslcd/ldap-schema.c b/nslcd/ldap-schema.c new file mode 100644 index 0000000..4ae95a5 --- /dev/null +++ b/nslcd/ldap-schema.c @@ -0,0 +1,453 @@ +/* + ldap-schema.c - LDAP schema information functions and definitions + This file was part of the nss_ldap library which has been + forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdlib.h> +#include <unistd.h> +#include <netdb.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include "ldap-nss.h" +#include "ldap-schema.h" +#include "util.h" + +/* max number of attributes per object class */ +#define ATTRTAB_SIZE 15 + +/** + * declare filters formerly declared in ldap-*.h + */ + +/* rfc822 mail aliases */ +char _nss_ldap_filt_getaliasbyname[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getaliasent[LDAP_FILT_MAXSIZ]; + +/* boot parameters */ +char _nss_ldap_filt_getbootparamsbyname[LDAP_FILT_MAXSIZ]; + +/* MAC address mappings */ +char _nss_ldap_filt_gethostton[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getntohost[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getetherent[LDAP_FILT_MAXSIZ]; + +/* groups */ +char _nss_ldap_filt_getgrnam[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getgrgid[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getgrent[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getgroupsbymemberanddn[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getgroupsbydn[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getpwnam_groupsbymember[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getgroupsbymember[LDAP_FILT_MAXSIZ]; + +/* IP hosts */ +char _nss_ldap_filt_gethostbyname[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_gethostbyaddr[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_gethostent[LDAP_FILT_MAXSIZ]; + +/* IP networks */ +char _nss_ldap_filt_getnetbyname[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getnetbyaddr[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getnetent[LDAP_FILT_MAXSIZ]; + +/* IP protocols */ +char _nss_ldap_filt_getprotobyname[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getprotobynumber[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getprotoent[LDAP_FILT_MAXSIZ]; + +/* users */ +char _nss_ldap_filt_getpwnam[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getpwuid[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getpwent[LDAP_FILT_MAXSIZ]; + +/* RPCs */ +char _nss_ldap_filt_getrpcbyname[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getrpcbynumber[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getrpcent[LDAP_FILT_MAXSIZ]; + +/* IP services */ +char _nss_ldap_filt_getservbyname[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getservbynameproto[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getservbyport[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getservbyportproto[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getservent[LDAP_FILT_MAXSIZ]; + +/* shadow users */ +char _nss_ldap_filt_getspnam[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_getspent[LDAP_FILT_MAXSIZ]; + +/* netgroups */ +char _nss_ldap_filt_getnetgrent[LDAP_FILT_MAXSIZ]; +char _nss_ldap_filt_innetgr[LDAP_FILT_MAXSIZ]; + +/** + * lookup filter initialization + */ +void +_nss_ldap_init_filters () +{ + /* rfc822 mail aliases */ + snprintf (_nss_ldap_filt_getaliasbyname, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (nisMailAlias), + ATM (LM_ALIASES, cn), "%s"); + snprintf (_nss_ldap_filt_getaliasent, LDAP_FILT_MAXSIZ, + "(%s=%s)", AT (objectClass), OC (nisMailAlias)); + + /* boot parameters */ + snprintf (_nss_ldap_filt_getbootparamsbyname, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (bootableDevice), + ATM (LM_BOOTPARAMS, cn), "%d"); + + /* MAC address mappings */ + snprintf (_nss_ldap_filt_gethostton, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (ieee802Device), + ATM (LM_ETHERS, cn), "%s"); + snprintf (_nss_ldap_filt_getntohost, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (ieee802Device), AT (macAddress), + "%s"); + snprintf (_nss_ldap_filt_getetherent, LDAP_FILT_MAXSIZ, "(%s=%s)", + AT (objectClass), OC (ieee802Device)); + + /* groups */ + snprintf (_nss_ldap_filt_getgrnam, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (posixGroup), + ATM (LM_GROUP, cn), "%s"); + snprintf (_nss_ldap_filt_getgrgid, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (posixGroup), + ATM (LM_GROUP, gidNumber), "%d"); + snprintf (_nss_ldap_filt_getgrent, LDAP_FILT_MAXSIZ, "(&(%s=%s))", + AT (objectClass), OC (posixGroup)); + snprintf (_nss_ldap_filt_getgroupsbymemberanddn, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(|(%s=%s)(%s=%s)))", + AT (objectClass), OC (posixGroup), AT (memberUid), "%s", AT (uniqueMember), "%s"); + snprintf (_nss_ldap_filt_getgroupsbydn, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", + AT (objectClass), OC (posixGroup), AT (uniqueMember), "%s"); + snprintf (_nss_ldap_filt_getpwnam_groupsbymember, LDAP_FILT_MAXSIZ, + "(|(&(%s=%s)(%s=%s))(&(%s=%s)(%s=%s)))", + AT (objectClass), OC (posixGroup), AT (memberUid), "%s", + AT (objectClass), OC (posixAccount), ATM (LM_PASSWD, uid), "%s"); + snprintf (_nss_ldap_filt_getgroupsbymember, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (posixGroup), AT (memberUid), + "%s"); + + /* IP hosts */ + snprintf (_nss_ldap_filt_gethostbyname, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (ipHost), ATM (LM_HOSTS, cn), + "%s"); + snprintf (_nss_ldap_filt_gethostbyaddr, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (ipHost), AT (ipHostNumber), + "%s"); + snprintf (_nss_ldap_filt_gethostent, LDAP_FILT_MAXSIZ, "(%s=%s)", + AT (objectClass), OC (ipHost)); + + /* IP networks */ + snprintf (_nss_ldap_filt_getnetbyname, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (ipNetwork), + ATM (LM_NETWORKS, cn), "%s"); + snprintf (_nss_ldap_filt_getnetbyaddr, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (ipNetwork), + AT (ipNetworkNumber), "%s"); + snprintf (_nss_ldap_filt_getnetent, LDAP_FILT_MAXSIZ, "(%s=%s)", + AT (objectClass), OC (ipNetwork)); + + /* IP protocols */ + snprintf (_nss_ldap_filt_getprotobyname, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (ipProtocol), + ATM (LM_PROTOCOLS, cn), "%s"); + snprintf (_nss_ldap_filt_getprotobynumber, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (ipProtocol), + AT (ipProtocolNumber), "%d"); + snprintf (_nss_ldap_filt_getprotoent, LDAP_FILT_MAXSIZ, "(%s=%s)", + AT (objectClass), OC (ipProtocol)); + + /* users */ + snprintf (_nss_ldap_filt_getpwnam, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (posixAccount), + ATM (LM_PASSWD, uid), "%s"); + snprintf (_nss_ldap_filt_getpwuid, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", + AT (objectClass), OC (posixAccount), AT (uidNumber), "%d"); + snprintf (_nss_ldap_filt_getpwent, LDAP_FILT_MAXSIZ, + "(%s=%s)", AT (objectClass), OC (posixAccount)); + + /* RPCs */ + snprintf (_nss_ldap_filt_getrpcbyname, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (oncRpc), ATM (LM_RPC, cn), "%s"); + snprintf (_nss_ldap_filt_getrpcbynumber, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (oncRpc), AT (oncRpcNumber), + "%d"); + snprintf (_nss_ldap_filt_getrpcent, LDAP_FILT_MAXSIZ, "(%s=%s)", + AT (objectClass), OC (oncRpc)); + + /* IP services */ + snprintf (_nss_ldap_filt_getservbyname, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (ipService), ATM (LM_SERVICES, cn), + "%s"); + snprintf (_nss_ldap_filt_getservbynameproto, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s)(%s=%s))", + AT (objectClass), OC (ipService), ATM (LM_SERVICES, cn), "%s", AT (ipServiceProtocol), + "%s"); + snprintf (_nss_ldap_filt_getservbyport, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (ipService), AT (ipServicePort), + "%d"); + snprintf (_nss_ldap_filt_getservbyportproto, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s)(%s=%s))", AT (objectClass), OC (ipService), + AT (ipServicePort), "%d", AT (ipServiceProtocol), "%s"); + snprintf (_nss_ldap_filt_getservent, LDAP_FILT_MAXSIZ, "(%s=%s)", + AT (objectClass), OC (ipService)); + + /* shadow users */ + snprintf (_nss_ldap_filt_getspnam, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (shadowAccount), + ATM (LM_SHADOW, uid), "%s"); + snprintf (_nss_ldap_filt_getspent, LDAP_FILT_MAXSIZ, + "(%s=%s)", AT (objectClass), OC (shadowAccount)); + + /* netgroups */ + snprintf (_nss_ldap_filt_getnetgrent, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (nisNetgroup), + ATM (LM_NETGROUP, cn), "%s"); + snprintf (_nss_ldap_filt_innetgr, LDAP_FILT_MAXSIZ, + "(&(%s=%s)(%s=%s))", AT (objectClass), OC (nisNetgroup), AT (memberNisNetgroup), "%s"); + +} + +static void init_pwd_attributes (const char ***pwd_attrs); +static void init_sp_attributes (const char ***sp_attrs); +static void init_grp_attributes (const char ***grp_attrs); +static void init_hosts_attributes (const char ***hosts_attrs); +static void init_services_attributes (const char ***services_attrs); +static void init_network_attributes (const char ***network_attrs); +static void init_proto_attributes (const char ***proto_attrs); +static void init_rpc_attributes (const char ***rpc_attrs); +static void init_ethers_attributes (const char ***ethers_attrs); +static void init_bp_attributes (const char ***bp_attrs); +static void init_alias_attributes (const char ***alias_attrs); +static void init_netgrp_attributes (const char ***netgrp_attrs); + +/** + * attribute table initialization routines + */ +void +_nss_ldap_init_attributes (const char ***attrtab) +{ + init_pwd_attributes (&attrtab[LM_PASSWD]); + init_sp_attributes (&attrtab[LM_SHADOW]); + init_grp_attributes (&attrtab[LM_GROUP]); + init_hosts_attributes (&attrtab[LM_HOSTS]); + init_services_attributes (&attrtab[LM_SERVICES]); + init_network_attributes (&attrtab[LM_NETWORKS]); + init_proto_attributes (&attrtab[LM_PROTOCOLS]); + init_rpc_attributes (&attrtab[LM_RPC]); + init_ethers_attributes (&attrtab[LM_ETHERS]); + init_network_attributes (&attrtab[LM_NETMASKS]); + init_bp_attributes (&attrtab[LM_BOOTPARAMS]); + init_alias_attributes (&attrtab[LM_ALIASES]); + init_netgrp_attributes (&attrtab[LM_NETGROUP]); + + attrtab[LM_NONE] = NULL; +} + +static void +init_pwd_attributes (const char ***pwd_attrs) +{ + int i = 0; + static const char *__pwd_attrs[ATTRTAB_SIZE + 1]; + + (*pwd_attrs) = __pwd_attrs; + + (*pwd_attrs)[i++] = ATM (LM_PASSWD, uid); + (*pwd_attrs)[i++] = ATM (LM_PASSWD, userPassword); + (*pwd_attrs)[i++] = AT (uidNumber); + (*pwd_attrs)[i++] = ATM (LM_PASSWD, gidNumber); + (*pwd_attrs)[i++] = ATM (LM_PASSWD, cn); + (*pwd_attrs)[i++] = AT (homeDirectory); + (*pwd_attrs)[i++] = AT (loginShell); + (*pwd_attrs)[i++] = AT (gecos); + (*pwd_attrs)[i++] = ATM (LM_PASSWD, description); + (*pwd_attrs)[i++] = AT (objectClass); + (*pwd_attrs)[i] = NULL; +} + +static void +init_sp_attributes (const char ***sp_attrs) +{ + static const char *__sp_attrs[ATTRTAB_SIZE + 1]; + + (*sp_attrs) = __sp_attrs; + + (*sp_attrs)[0] = (char *) ATM (LM_SHADOW, uid); + (*sp_attrs)[1] = (char *) ATM (LM_SHADOW, userPassword); + (*sp_attrs)[2] = (char *) AT (shadowLastChange); + (*sp_attrs)[3] = (char *) AT (shadowMax); + (*sp_attrs)[4] = (char *) AT (shadowMin); + (*sp_attrs)[5] = (char *) AT (shadowWarning); + (*sp_attrs)[6] = (char *) AT (shadowInactive); + (*sp_attrs)[7] = (char *) AT (shadowExpire); + (*sp_attrs)[8] = (char *) AT (shadowFlag); + (*sp_attrs)[9] = NULL; +} + +static void +init_grp_attributes (const char ***grp_attrs) +{ + int i = 0; + static const char *__grp_attrs[ATTRTAB_SIZE + 1]; + + (*grp_attrs) = __grp_attrs; + + (*grp_attrs)[i++] = (char *) ATM (LM_GROUP, cn); + (*grp_attrs)[i++] = (char *) ATM (LM_GROUP, userPassword); + (*grp_attrs)[i++] = (char *) AT (memberUid); + if (_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_RFC2307BIS)) + (*grp_attrs)[i++] = (char *) AT (uniqueMember); + (*grp_attrs)[i++] = (char *) ATM (LM_GROUP, gidNumber); + (*grp_attrs)[i] = NULL; +} + +static void +init_hosts_attributes (const char ***hosts_attrs) +{ + static const char *__hosts_attrs[ATTRTAB_SIZE + 1]; + + (*hosts_attrs) = __hosts_attrs; + + (*hosts_attrs)[0] = (char *) ATM (LM_HOSTS, cn); + (*hosts_attrs)[1] = (char *) AT (ipHostNumber); + (*hosts_attrs)[2] = NULL; +} + +static void +init_services_attributes (const char ***services_attrs) +{ + static const char *__services_attrs[ATTRTAB_SIZE + 1]; + + (*services_attrs) = __services_attrs; + + (*services_attrs)[0] = ATM (LM_SERVICES, cn); + (*services_attrs)[1] = AT (ipServicePort); + (*services_attrs)[2] = AT (ipServiceProtocol); + (*services_attrs)[3] = NULL; +} + +static void +init_network_attributes (const char ***network_attrs) +{ + static const char *__network_attrs[ATTRTAB_SIZE + 1]; + + (*network_attrs) = __network_attrs; + + (*network_attrs)[0] = ATM (LM_NETWORKS, cn); + (*network_attrs)[1] = AT (ipNetworkNumber); + (*network_attrs)[2] = AT (ipNetmaskNumber); + (*network_attrs)[3] = NULL; +} + +static void +init_proto_attributes (const char ***proto_attrs) +{ + static const char *__proto_attrs[ATTRTAB_SIZE + 1]; + + (*proto_attrs) = __proto_attrs; + + (*proto_attrs)[0] = ATM (LM_PROTOCOLS, cn); + (*proto_attrs)[1] = AT (ipProtocolNumber); + (*proto_attrs)[2] = NULL; +} + +static void +init_rpc_attributes (const char ***rpc_attrs) +{ + static const char *__rpc_attrs[ATTRTAB_SIZE + 1]; + + (*rpc_attrs) = __rpc_attrs; + + (*rpc_attrs)[0] = ATM (LM_RPC, cn); + (*rpc_attrs)[1] = AT (oncRpcNumber); + (*rpc_attrs)[2] = NULL; +} + +static void +init_ethers_attributes (const char ***ethers_attrs) +{ + static const char *__ethers_attrs[ATTRTAB_SIZE + 1]; + + (*ethers_attrs) = __ethers_attrs; + + (*ethers_attrs)[0] = ATM (LM_ETHERS, cn); + (*ethers_attrs)[1] = AT (macAddress); + (*ethers_attrs)[2] = NULL; +} + +static void +init_bp_attributes (const char ***bp_attrs) +{ + static const char *__bp_attrs[ATTRTAB_SIZE + 1]; + + (*bp_attrs) = __bp_attrs; + + (*bp_attrs)[0] = ATM (LM_BOOTPARAMS, cn); + (*bp_attrs)[1] = AT (bootParameter); + (*bp_attrs)[2] = NULL; +} + +static void +init_alias_attributes (const char ***alias_attrs) +{ + static const char *__alias_attrs[ATTRTAB_SIZE + 1]; + + (*alias_attrs) = __alias_attrs; + + (*alias_attrs)[0] = ATM (LM_ALIASES, cn); + (*alias_attrs)[1] = AT (rfc822MailMember); + (*alias_attrs)[2] = NULL; +} + +static void +init_netgrp_attributes (const char ***netgrp_attrs) +{ + static const char *__netgrp_attrs[ATTRTAB_SIZE + 1]; + + (*netgrp_attrs) = __netgrp_attrs; + + (*netgrp_attrs)[0] = ATM (LM_NETGROUP, cn); + (*netgrp_attrs)[1] = AT (nisNetgroupTriple); + (*netgrp_attrs)[2] = AT (memberNisNetgroup); + (*netgrp_attrs)[3] = NULL; +} diff --git a/nslcd/ldap-schema.h b/nslcd/ldap-schema.h new file mode 100644 index 0000000..558c1aa --- /dev/null +++ b/nslcd/ldap-schema.h @@ -0,0 +1,303 @@ +/* + ldap-schema.h - LDAP schema information functions and definitions + This file was part of the nss_ldap library which has been + forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#ifndef _LDAP_NSS_LDAP_LDAP_SCHEMA_H +#define _LDAP_NSS_LDAP_LDAP_SCHEMA_H + +/** + * function to initialize global lookup filters. + */ +void _nss_ldap_init_filters(void); +void _nss_ldap_init_attributes(const char ***attrtab); + +/** + * make filters formerly declared in ldap-*.h globally available. + */ + +/* rfc822 mail aliases */ +extern char _nss_ldap_filt_getaliasbyname[]; +extern char _nss_ldap_filt_getaliasent[]; + +/* boot parameters */ +extern char _nss_ldap_filt_getbootparamsbyname[]; + +/* MAC address mappings */ +extern char _nss_ldap_filt_gethostton[]; +extern char _nss_ldap_filt_getntohost[]; +extern char _nss_ldap_filt_getetherent[]; + +/* groups */ +extern char _nss_ldap_filt_getgrnam[]; +extern char _nss_ldap_filt_getgrgid[]; +extern char _nss_ldap_filt_getgrent[]; +extern char _nss_ldap_filt_getgroupsbymemberanddn[]; +extern char _nss_ldap_filt_getgroupsbydn[]; +extern char _nss_ldap_filt_getpwnam_groupsbymember[]; +extern char _nss_ldap_filt_getgroupsbymember[]; + +/* IP hosts */ +extern char _nss_ldap_filt_gethostbyname[]; +extern char _nss_ldap_filt_gethostbyaddr[]; +extern char _nss_ldap_filt_gethostent[]; + +/* IP networks */ +extern char _nss_ldap_filt_getnetbyname[]; +extern char _nss_ldap_filt_getnetbyaddr[]; +extern char _nss_ldap_filt_getnetent[]; + +/* IP protocols */ +extern char _nss_ldap_filt_getprotobyname[]; +extern char _nss_ldap_filt_getprotobynumber[]; +extern char _nss_ldap_filt_getprotoent[]; + +/* users */ +extern char _nss_ldap_filt_getpwnam[]; +extern char _nss_ldap_filt_getpwuid[]; +extern char _nss_ldap_filt_getpwent[]; + +/* RPCs */ +extern char _nss_ldap_filt_getrpcbyname[]; +extern char _nss_ldap_filt_getrpcbynumber[]; +extern char _nss_ldap_filt_getrpcent[]; + +/* IP services */ +extern char _nss_ldap_filt_getservbyname[]; +extern char _nss_ldap_filt_getservbynameproto[]; +extern char _nss_ldap_filt_getservbyport[]; +extern char _nss_ldap_filt_getservbyportproto[]; +extern char _nss_ldap_filt_getservent[]; + +/* shadow users */ +extern char _nss_ldap_filt_getspnam[]; +extern char _nss_ldap_filt_getspent[]; + +/* netgroups */ +extern char _nss_ldap_filt_getnetgrent[]; +extern char _nss_ldap_filt_innetgr[]; + +/** + * Initialize attribute vector table indexed by map + * selector (eg. LM_PASSWD) relative to an "ldap_config" + */ + +/** + * Lookup (potentially mapped) + * objectclass/attribute. + */ +#define OC(oc) _nss_ldap_map_oc(LM_NONE, OC##_##oc) +#define OCM(map, at) _nss_ldap_map_oc(map, AT##_##at) +#define AT(at) _nss_ldap_map_at(LM_NONE, AT##_##at) +#define ATM(map, at) _nss_ldap_map_at(map, AT##_##at) +#define DF(at) _nss_ldap_map_df(at) +#define OV(at) _nss_ldap_map_ov(at) + +/** + * Common attributes, not from RFC 2307. + */ +#define AT_objectClass "objectClass" +#define AT_cn "cn" +#define AT_description "description" +#define AT_l "l" +#define AT_manager "manager" + +/** + * Vendor-specific attributes and object classes. + * (Mainly from Sun.) + */ +#define OC_nisMailAlias "nisMailAlias" +#define AT_rfc822MailMember "rfc822MailMember" + +/** + * RFC 2307 attributes and object classes. + */ + +/* + * ( 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 ) ) + */ +#define OC_posixAccount "posixAccount" +#define AT_uid "uid" +#define AT_userPassword "userPassword" +#define AT_uidNumber "uidNumber" +#define AT_gidNumber "gidNumber" +#define AT_loginShell "loginShell" +#define AT_gecos "gecos" +#define AT_homeDirectory "homeDirectory" + +/* + * ( 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 ) ) + */ +#define OC_shadowAccount "shadowAccount" +#define AT_shadowLastChange "shadowLastChange" +#define AT_shadowMin "shadowMin" +#define AT_shadowMax "shadowMax" +#define AT_shadowWarning "shadowWarning" +#define AT_shadowInactive "shadowInactive" +#define AT_shadowExpire "shadowExpire" +#define AT_shadowFlag "shadowFlag" + +/* + * ( nisSchema.2.2 NAME 'posixGroup' SUP top STRUCTURAL + * DESC 'Abstraction of a group of accounts' + * MUST ( cn $ gidNumber ) + * MAY ( userPassword $ uidMember $ description ) ) + */ +#define OC_posixGroup "posixGroup" +#define AT_gidNumber "gidNumber" +#define AT_memberUid "memberUid" +#define AT_uniqueMember "uniqueMember" +#define AT_memberOf "memberOf" + +/* + * ( 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 ) ) + */ +#define OC_ipService "ipService" +#define AT_ipServicePort "ipServicePort" +#define AT_ipServiceProtocol "ipServiceProtocol" + +/* + * ( 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 ) + */ +#define OC_ipProtocol "ipProtocol" +#define AT_ipProtocolNumber "ipProtocolNumber" + +/* + * ( 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 ) + */ +#define OC_oncRpc "oncRpc" +#define AT_oncRpcNumber "oncRpcNumber" + +/* + * ( 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 ) ) + */ +#define OC_ipHost "ipHost" +#define AT_ipHostNumber "ipHostNumber" + +/* + * ( 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 ) ) + */ +#define OC_ipNetwork "ipNetwork" +#define AT_ipNetworkNumber "ipNetworkNumber" +#define AT_ipNetmaskNumber "ipNetmaskNumber" + +/* + * ( nisSchema.2.8 NAME 'nisNetgroup' SUP top STRUCTURAL + * DESC 'Abstraction of a netgroup. May refer to other netgroups' + * MUST cn + * MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) ) + */ +#define OC_nisNetgroup "nisNetgroup" +#define AT_nisNetgroupTriple "nisNetgroupTriple" +#define AT_memberNisNetgroup "memberNisNetgroup" + +/* + * ( nisSchema.2.09 NAME 'nisMap' SUP top STRUCTURAL + * DESC 'A generic abstraction of a NIS map' + * MUST nisMapName + * MAY description ) + */ +#define OC_nisMap "nisMap" +#define AT_nisMapName "nisNapName" + +/* + * ( nisSchema.2.10 NAME 'nisObject' SUP top STRUCTURAL + * DESC 'An entry in a NIS map' + * MUST ( cn $ nisMapEntry $ nisMapName ) + * MAY description ) + */ +#define OC_nisObject "nisObject" +#define AT_nisMapEntry "nisMapEntry" + +/* + * ( 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 ) + */ +#define OC_ieee802Device "ieee802Device" +#define AT_macAddress "macAddress" + +/* + * ( nisSchema.2.12 NAME 'bootableDevice' SUP top AUXILIARY + * DESC 'A device with boot parameters; device SHOULD be + * used as a structural class' + * MAY ( bootFile $ bootParameter ) ) + */ +#define OC_bootableDevice "bootableDevice" +#define AT_bootFile "bootFile" +#define AT_bootParameter "bootParameter" + +/* + * Map names + */ +#define MP_passwd "passwd" +#define MP_shadow "shadow" +#define MP_group "group" +#define MP_hosts "hosts" +#define MP_services "services" +#define MP_networks "networks" +#define MP_protocols "protocols" +#define MP_rpc "rpc" +#define MP_ethers "ethers" +#define MP_netmasks "netmasks" +#define MP_bootparams "bootparams" +#define MP_aliases "aliases" +#define MP_netgroup "netgroup" + +#endif /* _LDAP_NSS_LDAP_LDAP_SCHEMA_H */ diff --git a/nslcd/log.c b/nslcd/log.c new file mode 100644 index 0000000..c7370cf --- /dev/null +++ b/nslcd/log.c @@ -0,0 +1,187 @@ +/* + log.c - logging funtions + + Copyright (C) 2002, 2003 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + + +#include "config.h" + +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <syslog.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> + +#include "log.h" +#include "xmalloc.h" + + +/* set the logname */ +#undef PACKAGE +#define PACKAGE "nslcd" + + +/* storage for logging modes */ +static struct cvsd_log { + FILE *fp; /* NULL==syslog */ + int loglevel; + struct cvsd_log *next; +} *cvsd_loglist=NULL; + + +/* default loglevel when no logging is configured */ +static int prelogging_loglevel=LOG_INFO; + + +/* set loglevel when no logging is configured */ +void log_setdefaultloglevel(int loglevel) +{ + prelogging_loglevel=loglevel; +} + + +/* add logging method to configuration list */ +static void log_addlogging_fp(FILE *fp,int loglevel) +{ + struct cvsd_log *tmp,*lst; + /* create new logstruct */ + tmp=(struct cvsd_log *)xmalloc(sizeof(struct cvsd_log)); + tmp->fp=fp; + tmp->loglevel=loglevel; + tmp->next=NULL; + /* save the struct in the list */ + if (cvsd_loglist==NULL) + cvsd_loglist=tmp; + else + { + for (lst=cvsd_loglist;lst->next!=NULL;lst=lst->next); + lst->next=tmp; + } +} + + +/* configure logging to a file */ +void log_addlogging_file(const char *filename,int loglevel) +{ + FILE *fp; + fp=fopen(filename,"a"); + if (fp==NULL) + { + log_log(LOG_ERR,"cannot open logfile (%s) for appending: %s",filename,strerror(errno)); + exit(1); + } + log_addlogging_fp(fp,loglevel); +} + + +/* configure logging to syslog */ +void log_addlogging_syslog(int loglevel) +{ + openlog(PACKAGE,LOG_PID,LOG_DAEMON); + log_addlogging_fp(NULL,loglevel); +} + + +/* configure a null logging mode (no logging) */ +void log_addlogging_none() +{ + /* this is a hack, but it's so easy */ + log_addlogging_fp(NULL,LOG_EMERG); +} + + +/* start the logging with the configured logging methods + if no method is configured yet, logging is done to syslog */ +void log_startlogging(void) +{ + if (cvsd_loglist==NULL) + log_addlogging_syslog(LOG_INFO); + prelogging_loglevel=-1; +} + + +/* log the given message using the configured logging method */ +void log_log(int pri,const char *format, ...) +{ + int res; + struct cvsd_log *lst; + /* TODO: make this something better */ + #define maxbufferlen 120 + char buffer[maxbufferlen]; + va_list ap; + /* make the message */ + va_start(ap,format); + res=vsnprintf(buffer,maxbufferlen,format,ap); + if ((res<0)||(res>=maxbufferlen)) + { + /* truncate with "..." */ + buffer[maxbufferlen-2]='.'; + buffer[maxbufferlen-3]='.'; + buffer[maxbufferlen-4]='.'; + } + buffer[maxbufferlen-1]='\0'; + va_end(ap); + /* do the logging */ + if (prelogging_loglevel>=0) + { + /* if logging is not yet defined, log to stderr */ + if (pri<=prelogging_loglevel) + fprintf(stderr,"%s: %s%s\n",PACKAGE,pri==LOG_DEBUG?"DEBUG: ":"",buffer); + } + else + { + for (lst=cvsd_loglist;lst!=NULL;lst=lst->next) + { + if (pri<=lst->loglevel) + { + if (lst->fp==NULL) /* syslog */ + syslog(pri,"%s",buffer); + else /* file */ + { + fprintf(lst->fp,"%s: %s\n",PACKAGE,buffer); + fflush(lst->fp); + } + } + } + } +} + + +/* return the syslog loglevel represented by the string + return -1 on unknown */ +int log_getloglevel(const char *lvl) +{ + if ( strcmp(lvl,"crit")==0 ) + return LOG_CRIT; + else if ( (strcmp(lvl,"error")==0) || + (strcmp(lvl,"err")==0) ) + return LOG_ERR; + else if ( strcmp(lvl,"warning")==0 ) + return LOG_WARNING; + else if ( strcmp(lvl,"notice")==0 ) + return LOG_NOTICE; + else if ( strcmp(lvl,"info")==0 ) + return LOG_INFO; + else if ( strcmp(lvl,"debug")==0 ) + return LOG_DEBUG; + else + return -1; /* unknown */ +} diff --git a/nslcd/log.h b/nslcd/log.h new file mode 100644 index 0000000..4124fbe --- /dev/null +++ b/nslcd/log.h @@ -0,0 +1,60 @@ +/* + log.h - definitions of logging funtions + + Copyright (C) 2002, 2003 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + + +#ifndef _LOG_H +#define _LOG_H 1 + + +#include <syslog.h> + + +/* set loglevel when no logging is configured */ +void log_setdefaultloglevel(int loglevel); + + +/* configure logging to a file */ +void log_addlogging_file(const char *filename,int loglevel); + + +/* configure logging to syslog */ +void log_addlogging_syslog(int loglevel); + + +/* configure a null logging mode (no logging) */ +void log_addlogging_none(void); + + +/* start the logging with the configured logging methods + if no method is configured yet, logging is done to syslog */ +void log_startlogging(void); + + +/* log the given message using the configured logging method */ +void log_log(int pri,const char *format, ...); + + +/* return the syslog loglevel represented by the string + return -1 on unknown */ +int log_getloglevel(const char *lvl); + + +#endif /* not _LOG_H */ diff --git a/nslcd/netgroup.c b/nslcd/netgroup.c new file mode 100644 index 0000000..e79657d --- /dev/null +++ b/nslcd/netgroup.c @@ -0,0 +1,354 @@ +/* + netgroup.c - netgroup lookup routines + This file was part of the nss_ldap library (as ldap-netgrp.c) + which has been forked into the nss-ldapd library. + This file also contains code that is taken from the GNU C + Library (nss/nss_files/files-netgrp.c). + + Copyright (C) 1996, 1997, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/param.h> +#include <string.h> +#include <assert.h> +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +/* A netgroup can consist of names of other netgroups. We have to + track which netgroups were read and which still have to be read. */ + +/* Dataset for iterating netgroups. */ +struct __netgrent +{ + enum + { triple_val, group_val } + type; + + union + { + struct + { + const char *host; + const char *user; + const char *domain; + } + triple; + + const char *group; + } + val; + + /* Room for the data kept between the calls to the netgroup + functions. We must avoid global variables. */ + char *data; + size_t data_size; + char *cursor; + int first; + + struct name_list *known_groups; + struct name_list *needed_groups; +}; + +/* + * I (Luke Howard) pulled the following macro (EXPAND), functions + * (strip_whitespace and _nss_netgroup_parseline) and structures + * (name_list and __netgrent) from glibc-2.2.x. _nss_netgroup_parseline + * became _nss_ldap_parse_netgr after some modification. + * + * The rest of the code is modeled on various other _nss_ldap functions. + */ + +#define EXPAND(needed) \ + do \ + { \ + size_t old_cursor = result->cursor - result->data; \ + \ + result->data_size += 512 > 2 * needed ? 512 : 2 * needed; \ + result->data = realloc (result->data, result->data_size); \ + \ + if (result->data == NULL) \ + { \ + stat = NSS_STATUS_UNAVAIL; \ + goto out; \ + } \ + \ + result->cursor = result->data + old_cursor; \ + } \ + while (0) + +static char * +strip_whitespace (char *str) +{ + char *cp = str; + + /* Skip leading spaces. */ + while (isspace ((int) *cp)) + cp++; + + str = cp; + while (*cp != '\0' && !isspace ((int) *cp)) + cp++; + + /* Null-terminate, stripping off any trailing spaces. */ + *cp = '\0'; + + return *str == '\0' ? NULL : str; +} + +static enum nss_status +_nss_ldap_parse_netgr (void *vresultp, char *buffer, size_t buflen) +{ + struct __netgrent *result = (struct __netgrent *) vresultp; + char *cp = result->cursor; + char *user, *host, *domain; + + /* The netgroup either doesn't exist or is empty. */ + if (cp == NULL) + return NSS_STATUS_RETURN; + + /* First skip leading spaces. */ + while (isspace ((int) *cp)) + ++cp; + + if (*cp != '(') + { + /* We have a list of other netgroups. */ + char *name = cp; + + while (*cp != '\0' && !isspace ((int) *cp)) + ++cp; + + if (name != cp) + { + /* It is another netgroup name. */ + int last = *cp == '\0'; + + result->type = group_val; + result->val.group = name; + *cp = '\0'; + if (!last) + ++cp; + result->cursor = cp; + result->first = 0; + + return NSS_STATUS_SUCCESS; + } + return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN; + } + + /* Match host name. */ + host = ++cp; + while (*cp != ',') + if (*cp++ == '\0') + return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN; + + /* Match user name. */ + user = ++cp; + while (*cp != ',') + if (*cp++ == '\0') + return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN; + + /* Match domain name. */ + domain = ++cp; + while (*cp != ')') + if (*cp++ == '\0') + return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN; + ++cp; + + /* When we got here we have found an entry. Before we can copy it + to the private buffer we have to make sure it is big enough. */ + if (cp - host > buflen) + return NSS_STATUS_TRYAGAIN; + + strncpy (buffer, host, cp - host); + result->type = triple_val; + + buffer[(user - host) - 1] = '\0'; + result->val.triple.host = strip_whitespace (buffer); + + buffer[(domain - host) - 1] = '\0'; + result->val.triple.user = strip_whitespace (buffer + (user - host)); + + buffer[(cp - host) - 1] = '\0'; + result->val.triple.domain = strip_whitespace (buffer + (domain - host)); + + /* Remember where we stopped reading. */ + result->cursor = cp; + result->first = 0; + + return NSS_STATUS_SUCCESS; +} + +static enum nss_status +_nss_ldap_load_netgr (LDAPMessage * e, + struct ldap_state * pvt, + void *vresultp, char *buffer, size_t buflen) +{ + int attr; + int nvals; + int valcount = 0; + char **vals; + char **valiter; + struct __netgrent *result = vresultp; + enum nss_status stat = NSS_STATUS_SUCCESS; + + for (attr = 0; attr < 2; attr++) + { + switch (attr) + { + case 1: + vals = _nss_ldap_get_values (e, AT (nisNetgroupTriple)); + break; + default: + vals = _nss_ldap_get_values (e, AT (memberNisNetgroup)); + break; + } + + nvals = ldap_count_values (vals); + + if (vals == NULL) + continue; + + if (nvals == 0) + { + ldap_value_free (vals); + continue; + } + + if (result->data_size > 0 + && result->cursor - result->data + 1 > result->data_size) + EXPAND (1); + + if (result->data_size > 0) + *result->cursor++ = ' '; + + valcount += nvals; + valiter = vals; + + while (*valiter != NULL) + { + int curlen = strlen (*valiter); + if (result->cursor - result->data + curlen + 1 > result->data_size) + EXPAND (curlen + 1); + memcpy (result->cursor, *valiter, curlen + 1); + result->cursor += curlen; + valiter++; + if (*valiter != NULL) + *result->cursor++ = ' '; + } + ldap_value_free (vals); + } + + result->first = 1; + result->cursor = result->data; + +out: + + return stat; +} + +int nslcd_netgroup_byname(FILE *fp) +{ + + int32_t tmpint32; + static struct ent_context *netgroup_context=NULL; + char *name; + /* these are here for now until we rewrite the LDAP code */ + struct __netgrent result; + char buffer[1024]; + int errnop; + struct ldap_args a; + enum nss_status stat=NSS_STATUS_SUCCESS; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* log call */ + log_log(LOG_DEBUG,"nslcd_netgroup_byname(%s)",name); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_NETGROUP_BYNAME); + /* initialize structure */ + result.data=result.cursor=NULL; + result.data_size = 0; + /* do initial ldap request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + stat=_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getnetgrent,LM_NETGROUP,_nss_ldap_load_netgr); + if (_nss_ldap_ent_context_init(&netgroup_context)==NULL) + return -1; + /* loop over all results */ + while ((stat=_nss_ldap_parse_netgr(&result,buffer,1024))==NSS_STATUS_SUCCESS) + { + if (result.type==triple_val) + { + WRITE_INT32(fp,NSLCD_RESULT_SUCCESS); + WRITE_INT32(fp,NETGROUP_TYPE_TRIPLE); + if (result.val.triple.host==NULL) + { WRITE_STRING(fp,""); } + else + { WRITE_STRING(fp,result.val.triple.host); } + if (result.val.triple.user==NULL) + { WRITE_STRING(fp,""); } + else + { WRITE_STRING(fp,result.val.triple.user); } + if (result.val.triple.domain==NULL) + { WRITE_STRING(fp,""); } + else + { WRITE_STRING(fp,result.val.triple.domain); } + } + else if (result.type==group_val) + { + WRITE_INT32(fp,NSLCD_RESULT_SUCCESS); + WRITE_INT32(fp,NETGROUP_TYPE_NETGROUP); + WRITE_STRING(fp,result.val.group); + } + } + /* free data */ + if (result.data!=NULL) + free(result.data); + /* we're done */ + _nss_ldap_enter(); + _nss_ldap_ent_context_release(netgroup_context); + _nss_ldap_leave(); + return 0; +} + diff --git a/nslcd/network.c b/nslcd/network.c new file mode 100644 index 0000000..e44a3e1 --- /dev/null +++ b/nslcd/network.c @@ -0,0 +1,245 @@ +/* + network.c - network address entry lookup routines + This file was part of the nss_ldap library (as ldap-network.c) + which has been forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <sys/socket.h> +#include <errno.h> +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +#if defined(HAVE_USERSEC_H) +#define MAXALIASES 35 +#define MAXADDRSIZE 4 +#endif /* HAVE_USERSEC_H */ + +/* write a single network entry to the stream */ +static int write_netent(FILE *fp,struct netent *result) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + /* write the network name */ + WRITE_STRING(fp,result->n_name); + /* write the alias list */ + WRITE_STRINGLIST_NULLTERM(fp,result->n_aliases); + /* write the number of addresses */ + WRITE_INT32(fp,1); + /* write the addresses in network byte order */ + WRITE_INT32(fp,result->n_addrtype); + WRITE_INT32(fp,sizeof(unsigned long int)); + result->n_net=htonl(result->n_net); + WRITE_INT32(fp,result->n_net); + return 0; +} +static enum nss_status +_nss_ldap_parse_net (LDAPMessage * e, + struct ldap_state * pvt, + void *result, char *buffer, size_t buflen) +{ + + char *tmp; + struct netent *network = (struct netent *) result; + enum nss_status stat; + + /* IPv6 support ? XXX */ + network->n_addrtype = AF_INET; + + stat = _nss_ldap_assign_attrval (e, ATM (LM_NETWORKS, cn), &network->n_name, + &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = + _nss_ldap_assign_attrval (e, AT (ipNetworkNumber), &tmp, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + network->n_net = inet_network (tmp); + + stat = + _nss_ldap_assign_attrvals (e, ATM (LM_NETWORKS, cn), network->n_name, + &network->n_aliases, &buffer, &buflen, NULL); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + return NSS_STATUS_SUCCESS; +} + +int nslcd_network_byname(FILE *fp) +{ + int32_t tmpint32; + char *name; + struct ldap_args a; + int retv; + struct netent result; + char buffer[1024]; + int errnop; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* log call */ + log_log(LOG_DEBUG,"nslcd_network_byname(%s)",name); + /* write the response header */ + /* FIXME: free(name) when one of these writes fails */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_NETWORK_BYNAME); + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getnetbyname,LM_NETWORKS,_nss_ldap_parse_net)); + /* no more need for this string */ + free(name); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + write_netent(fp,&result); + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_network_byaddr(FILE *fp) +{ + int32_t tmpint32; + int af; + int len; + char addr[64],name[1024]; + struct ldap_args a; + int retv=456; + struct netent result; + char buffer[1024]; + int errnop; + /* read address family */ + READ_INT32(fp,af); + if (af!=AF_INET) + { + log_log(LOG_WARNING,"incorrect address family specified: %d",af); + return -1; + } + /* read address length */ + READ_INT32(fp,len); + if ((len>64)||(len<=0)) + { + log_log(LOG_WARNING,"address length incorrect: %d",len); + return -1; + } + /* read address */ + READ(fp,addr,len); + /* translate the address to a string */ + if (inet_ntop(af,addr,name,1024)==NULL) + { + log_log(LOG_WARNING,"unable to convert address to string"); + return -1; + } + /* log call */ + log_log(LOG_DEBUG,"nslcd_network_byaddr(%s)",name); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_NETWORK_BYADDR); + /* prepare the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + /* do requests until we find a result */ + /* TODO: probably do more sofisticated queries */ + while (retv==456) + { + /* do the request */ + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getnetbyaddr,LM_NETWORKS,_nss_ldap_parse_net)); + /* if no entry was found, retry with .0 stripped from the end */ + if ((retv==NSLCD_RESULT_NOTFOUND) && + (strlen(name)>2) && + (strncmp(name+strlen(name)-2,".0",2)==0)) + { + /* strip .0 and try again */ + name[strlen(name)-2]='\0'; + retv=456; + } + } + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + write_netent(fp,&result); + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_network_all(FILE *fp) +{ + int32_t tmpint32; + static struct ent_context *net_context; + /* these are here for now until we rewrite the LDAP code */ + struct netent result; + char buffer[1024]; + int errnop; + int retv; + /* log call */ + log_log(LOG_DEBUG,"nslcd_network_all()"); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_NETWORK_ALL); + /* initialize context */ + if (_nss_ldap_ent_context_init(&net_context)==NULL) + return -1; + /* loop over all results */ + while ((retv=nss2nslcd(_nss_ldap_getent(&net_context,&result,buffer,1024,&errnop,_nss_ldap_filt_getnetent,LM_NETWORKS,_nss_ldap_parse_net)))==NSLCD_RESULT_SUCCESS) + { + /* write the result */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + write_netent(fp,&result); + } + /* write the final result code */ + WRITE_INT32(fp,retv); + WRITE_FLUSH(fp); + /* FIXME: if a previous call returns what happens to the context? */ + _nss_ldap_enter(); + _nss_ldap_ent_context_release(net_context); + _nss_ldap_leave(); + /* we're done */ + return 0; +} diff --git a/nslcd/nslcd.c b/nslcd/nslcd.c new file mode 100644 index 0000000..48db1cf --- /dev/null +++ b/nslcd/nslcd.c @@ -0,0 +1,656 @@ +/* + nslcd.c - ldap local connection daemon + + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/wait.h> +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif /* HAVE_GETOPT_H */ +#include <assert.h> +#include <signal.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#ifdef HAVE_GRP_H +#include <grp.h> +#endif /* HAVE_GRP_H */ +#include <nss.h> +#include <pthread.h> + +#include "nslcd.h" +#include "log.h" +#include "common.h" + + +/* the definition of the environment */ +extern char **environ; + + +/* flag to indictate if we are in debugging mode */ +static int nslcd_debugging=0; + + +/* the exit flag to indicate that a signal was received */ +static volatile int nslcd_exitsignal=0; + + +/* the server socket used for communication */ +static int nslcd_serversocket=-1; + + +/* thread ids of all running threads */ +#define NUM_THREADS 5 +pthread_t nslcd_threads[NUM_THREADS]; + + +/* display version information */ +static void display_version(FILE *fp) +{ + fprintf(fp,"%s\n",PACKAGE_STRING); + fprintf(fp,"Written by Luke Howard and Arthur de Jong.\n\n"); + fprintf(fp,"Copyright (C) 1997-2006 Luke Howard, Arthur de Jong and West Consulting\n" + "This is free software; see the source for copying conditions. There is NO\n" + "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); +} + + +/* display usage information to stdout and exit(status) */ +static void display_usage(FILE *fp,const char *program_name) +{ + fprintf(fp,"Usage: %s [OPTION]...\n",program_name); + fprintf(fp,"Name Service LDAP connection daemon.\n"); + fprintf(fp," -d, --debug don't fork and print debugging to stderr\n"); + fprintf(fp," --help display this help and exit\n"); + fprintf(fp," --version output version information and exit\n"); + fprintf(fp,"\n" + "Report bugs to <%s>.\n",PACKAGE_BUGREPORT); +} + + +/* the definition of options for getopt(). see getopt(2) */ +static struct option const nslcd_options[] = +{ + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } +}; +#define NSLCD_OPTIONSTRING "dhV" + + +/* parse command line options and save settings in struct */ +static void parse_cmdline(int argc,char *argv[]) +{ + int optc; + while ((optc=getopt_long(argc,argv,NSLCD_OPTIONSTRING,nslcd_options,NULL))!=-1) + { + switch (optc) + { + case 'd': /* -d, --debug don't fork and print debugging to stderr */ + nslcd_debugging=1; + log_setdefaultloglevel(LOG_DEBUG); + break; + case 'h': /* --help display this help and exit */ + display_usage(stdout,argv[0]); + exit(0); + case 'V': /* --version output version information and exit */ + display_version(stdout); + exit(0); + case ':': /* missing required parameter */ + case '?': /* unknown option character or extraneous parameter */ + default: + fprintf(stderr,"Try `%s --help' for more information.\n", + argv[0]); + exit(1); + } + } + /* check for remaining arguments */ + if (optind<argc) + { + fprintf(stderr,"%s: unrecognized option `%s'\n", + argv[0],argv[optind]); + fprintf(stderr,"Try `%s --help' for more information.\n", + argv[0]); + exit(1); + } +} + + +/* get a name of a signal with a given signal number */ +static const char *signame(int signum) +{ + switch (signum) + { + case SIGHUP: return "SIGHUP"; /* Hangup detected */ + case SIGINT: return "SIGINT"; /* Interrupt from keyboard */ + case SIGQUIT: return "SIGQUIT"; /* Quit from keyboard */ + case SIGILL: return "SIGILL"; /* Illegal Instruction */ + case SIGABRT: return "SIGABRT"; /* Abort signal from abort(3) */ + case SIGFPE: return "SIGFPE"; /* Floating point exception */ + case SIGKILL: return "SIGKILL"; /* Kill signal */ + case SIGSEGV: return "SIGSEGV"; /* Invalid memory reference */ + case SIGPIPE: return "SIGPIPE"; /* Broken pipe */ + case SIGALRM: return "SIGALRM"; /* Timer signal from alarm(2) */ + case SIGTERM: return "SIGTERM"; /* Termination signal */ + case SIGUSR1: return "SIGUSR1"; /* User-defined signal 1 */ + case SIGUSR2: return "SIGUSR2"; /* User-defined signal 2 */ + case SIGCHLD: return "SIGCHLD"; /* Child stopped or terminated */ + case SIGCONT: return "SIGCONT"; /* Continue if stopped */ + case SIGSTOP: return "SIGSTOP"; /* Stop process */ + case SIGTSTP: return "SIGTSTP"; /* Stop typed at tty */ + case SIGTTIN: return "SIGTTIN"; /* tty input for background process */ + case SIGTTOU: return "SIGTTOU"; /* tty output for background process */ +#ifdef SIGBUS + case SIGBUS: return "SIGBUS"; /* Bus error */ +#endif +#ifdef SIGPOLL + case SIGPOLL: return "SIGPOLL"; /* Pollable event */ +#endif +#ifdef SIGPROF + case SIGPROF: return "SIGPROF"; /* Profiling timer expired */ +#endif +#ifdef SIGSYS + case SIGSYS: return "SIGSYS"; /* Bad argument to routine */ +#endif +#ifdef SIGTRAP + case SIGTRAP: return "SIGTRAP"; /* Trace/breakpoint trap */ +#endif +#ifdef SIGURG + case SIGURG: return "SIGURG"; /* Urgent condition on socket */ +#endif +#ifdef SIGVTALRM + case SIGVTALRM: return "SIGVTALRM"; /* Virtual alarm clock */ +#endif +#ifdef SIGXCPU + case SIGXCPU: return "SIGXCPU"; /* CPU time limit exceeded */ +#endif +#ifdef SIGXFSZ + case SIGXFSZ: return "SIGXFSZ"; /* File size limit exceeded */ +#endif + default: return "UNKNOWN"; + } +} + + +/* signal handler for closing down */ +static RETSIGTYPE sigexit_handler(int signum) +{ + int i; + nslcd_exitsignal=signum; + /* cancel all running threads */ + for (i=0;i<NUM_THREADS;i++) + pthread_cancel(nslcd_threads[i]); + +} + + +/* do some cleaning up before terminating */ +static void exithandler(void) +{ + /* close socket if it's still in use */ + if (nslcd_serversocket >= 0) + { + if (close(nslcd_serversocket)) + log_log(LOG_WARNING,"problem closing server socket (ignored): %s",strerror(errno)); + } + /* remove existing named socket */ + if (unlink(NSLCD_SOCKET)<0) + { + log_log(LOG_DEBUG,"unlink() of "NSLCD_SOCKET" failed (ignored): %s", + strerror(errno)); + } + /* remove pidfile */ + if (unlink(NSLCD_PIDFILE)<0) + { + log_log(LOG_DEBUG,"unlink() of "NSLCD_PIDFILE" failed (ignored): %s", + strerror(errno)); + } + /* log exit */ + log_log(LOG_INFO,"version %s bailing out",VERSION); +} + + +/* returns a socket ready to answer requests from the client, + exit()s on error */ +static int open_socket(void) +{ + int sock; + struct sockaddr_un addr; + + /* create a socket */ + if ( (sock=socket(PF_UNIX,SOCK_STREAM,0))<0 ) + { + log_log(LOG_ERR,"cannot create socket: %s",strerror(errno)); + exit(1); + } + + /* remove existing named socket */ + if (unlink(NSLCD_SOCKET)<0) + { + log_log(LOG_DEBUG,"unlink() of "NSLCD_SOCKET" failed (ignored): %s", + strerror(errno)); + } + + /* create socket address structure */ + memset(&addr,0,sizeof(struct sockaddr_un)); + addr.sun_family=AF_UNIX; + strcpy(addr.sun_path,NSLCD_SOCKET); + + /* bind to the named socket */ + if (bind(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr_un))) + { + log_log(LOG_ERR,"bind() to "NSLCD_SOCKET" failed: %s", + strerror(errno)); + if (close(sock)) + log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); + exit(1); + } + + /* close the file descriptor on exit */ + if (fcntl(sock,F_SETFD,FD_CLOEXEC)<0) + { + log_log(LOG_ERR,"fctnl(F_SETFL,O_NONBLOCK) failed: %s",strerror(errno)); + if (close(sock)) + log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); + exit(1); + } + + /* set permissions of socket so anybody can do requests */ + if (chmod(NSLCD_SOCKET,0666)) + { + log_log(LOG_ERR,"fctnl(F_SETFL,O_NONBLOCK) failed: %s",strerror(errno)); + if (close(sock)) + log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); + exit(1); + } + + /* start listening for connections */ + if (listen(sock,SOMAXCONN)<0) + { + log_log(LOG_ERR,"listen() failed: %s",strerror(errno)); + if (close(sock)) + log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); + exit(1); + } + + /* we're done */ + return sock; +} + +/* 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(FILE *fp,int32_t *action) +{ + int32_t tmpint32; + /* read the protocol version */ + READ_TYPE(fp,tmpint32,int32_t); + if (tmpint32 != NSLCD_VERSION) + { + log_log(LOG_DEBUG,"wrong nslcd version id (%d)",(int)tmpint32); + return -1; + } + /* read the request type */ + READ(fp,action,sizeof(int32_t)); + return 0; +} + +/* read a request message, returns <0 in case of errors, + this function closes the socket */ +static void handleconnection(int sock) +{ + FILE *fp; + socklen_t alen; + struct ucred client; + int32_t action; + + /* look up process information from client */ + alen=sizeof(struct ucred); + if (getsockopt(sock,SOL_SOCKET,SO_PEERCRED,&client,&alen) < 0) + { + log_log(LOG_ERR,"getsockopt(SO_PEERCRED) failed: %s",strerror(errno)); + if (close(sock)) + log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); + return; + } + + /* log connection */ + log_log(LOG_DEBUG,"connection from pid=%d uid=%d gid=%d", + (int)client.pid,(int)client.uid,(int)client.gid); + + /* create a stream object */ + if ((fp=fdopen(sock,"w+"))==NULL) + { + log_log(LOG_WARNING,"cannot create stream for writing: %s",strerror(errno)); + close(sock); + return; + } + + /* read request */ + if (read_header(fp,&action)) + { + fclose(fp); + return; + } + + /* handle request */ + switch (action) + { + case NSLCD_ACTION_ALIAS_BYNAME: nslcd_alias_byname(fp); break; + case NSLCD_ACTION_ALIAS_ALL: nslcd_alias_all(fp); break; + case NSLCD_ACTION_ETHER_BYNAME: nslcd_ether_byname(fp); break; + case NSLCD_ACTION_ETHER_BYETHER: nslcd_ether_byether(fp); break; + case NSLCD_ACTION_ETHER_ALL: nslcd_ether_all(fp); break; + case NSLCD_ACTION_GROUP_BYNAME: nslcd_group_byname(fp); break; + case NSLCD_ACTION_GROUP_BYGID: nslcd_group_bygid(fp); break; + case NSLCD_ACTION_GROUP_BYMEMBER: nslcd_group_bymember(fp); break; + case NSLCD_ACTION_GROUP_ALL: nslcd_group_all(fp); break; + case NSLCD_ACTION_HOST_BYNAME: nslcd_host_byname(fp); break; + case NSLCD_ACTION_HOST_BYADDR: nslcd_host_byaddr(fp); break; + case NSLCD_ACTION_HOST_ALL: nslcd_host_all(fp); break; + case NSLCD_ACTION_NETGROUP_BYNAME: nslcd_netgroup_byname(fp); break; + case NSLCD_ACTION_NETWORK_BYNAME: nslcd_network_byname(fp); break; + case NSLCD_ACTION_NETWORK_BYADDR: nslcd_network_byaddr(fp); break; + case NSLCD_ACTION_NETWORK_ALL: nslcd_network_all(fp); break; + case NSLCD_ACTION_PASSWD_BYNAME: nslcd_passwd_byname(fp); break; + case NSLCD_ACTION_PASSWD_BYUID: nslcd_passwd_byuid(fp); break; + case NSLCD_ACTION_PASSWD_ALL: nslcd_passwd_all(fp); break; + case NSLCD_ACTION_PROTOCOL_BYNAME: nslcd_protocol_byname(fp); break; + case NSLCD_ACTION_PROTOCOL_BYNUMBER:nslcd_protocol_bynumber(fp); break; + case NSLCD_ACTION_PROTOCOL_ALL: nslcd_protocol_all(fp); break; + case NSLCD_ACTION_RPC_BYNAME: nslcd_rpc_byname(fp); break; + case NSLCD_ACTION_RPC_BYNUMBER: nslcd_rpc_bynumber(fp); break; + case NSLCD_ACTION_RPC_ALL: nslcd_rpc_all(fp); break; + case NSLCD_ACTION_SERVICE_BYNAME: nslcd_service_byname(fp); break; + case NSLCD_ACTION_SERVICE_BYNUMBER: nslcd_service_bynumber(fp); break; + case NSLCD_ACTION_SERVICE_ALL: nslcd_service_all(fp); break; + case NSLCD_ACTION_SHADOW_BYNAME: nslcd_shadow_byname(fp); break; + case NSLCD_ACTION_SHADOW_ALL: nslcd_shadow_all(fp); break; + default: + log_log(LOG_WARNING,"invalid request id: %d",(int)action); + break; + } + + /* we're done with the request */ + fclose(fp); + return; +} + +/* accept a connection on the socket */ +static void acceptconnection(void) +{ + int csock; + int j; + struct sockaddr_storage addr; + socklen_t alen; + + /* accept a new connection */ + alen=(socklen_t)sizeof(struct sockaddr_storage); + csock=accept(nslcd_serversocket,(struct sockaddr *)&addr,&alen); + if (csock<0) + { + if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK)) + { + log_log(LOG_DEBUG,"debug: accept() failed (ignored): %s",strerror(errno)); + return; + } + log_log(LOG_ERR,"accept() failed: %s",strerror(errno)); + return; + } + + /* make sure O_NONBLOCK is not inherited */ + if ((j=fcntl(csock,F_GETFL,0))<0) + { + log_log(LOG_ERR,"fctnl(F_GETFL) failed: %s",strerror(errno)); + if (close(csock)) + log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); + return; + } + if (fcntl(csock,F_SETFL,j&~O_NONBLOCK)<0) + { + log_log(LOG_ERR,"fctnl(F_SETFL,~O_NONBLOCK) failed: %s",strerror(errno)); + if (close(csock)) + log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); + return; + } + + /* handle the connection */ + handleconnection(csock); +} + + +/* write the current process id to the specified file */ +static void write_pidfile(const char *filename) +{ + FILE *fp; + if (filename!=NULL) + { + if ((fp=fopen(filename,"w"))==NULL) + { + log_log(LOG_ERR,"cannot open pid file (%s): %s",filename,strerror(errno)); + exit(1); + } + if (fprintf(fp,"%d\n",(int)getpid())<=0) + { + log_log(LOG_ERR,"error writing pid file (%s)",filename); + exit(1); + } + if (fclose(fp)) + { + log_log(LOG_ERR,"error writing pid file (%s): %s",filename,strerror(errno)); + exit(1); + } + } +} + + +/* try to install signal handler and check result */ +static void install_sighandler(int signum,RETSIGTYPE (*handler) (int)) +{ + struct sigaction act; + memset(&act,0,sizeof(struct sigaction)); + act.sa_handler=handler; + sigemptyset(&act.sa_mask); + act.sa_flags=SA_RESTART|SA_NOCLDSTOP; + if (sigaction(signum,&act,NULL)!=0) + { + log_log(LOG_ERR,"error installing signal handler for '%s': %s",signame(signum),strerror(errno)); + exit(1); + } +} + +static void *worker(void *arg) +{ + /* start waiting for incoming connections */ + while (nslcd_exitsignal==0) + { + /* wait for a new connection */ + acceptconnection(); + } + return NULL; +} + + +/* the main program... */ +int main(int argc,char *argv[]) +{ + gid_t mygid=-1; + uid_t myuid=-1; + int i; + + /* parse the command line */ + parse_cmdline(argc,argv); + + /* clear the environment */ + /* TODO:implement */ + + /* check if we are already running */ + /* FIXME: implement (maybe pass along options or commands) */ + + /* disable ldap lookups of host names to avoid lookup loop + and fall back to files dns (a sensible default) */ + /* TODO: parse /etc/nsswitch ourselves and just remove ldap from the list */ + if (__nss_configure_lookup("hosts","files dns")) + log_log(LOG_ERR,"unable to override hosts lookup method: %s",strerror(errno)); + + /* daemonize */ + if ((!nslcd_debugging)&&(daemon(0,0)<0)) + { + log_log(LOG_ERR,"unable to daemonize: %s",strerror(errno)); + exit(1); + } + + /* set default mode for pidfile and socket */ + umask(0022); + + /* intilialize logging */ + if (!nslcd_debugging) + log_startlogging(); + log_log(LOG_INFO,"version %s starting",VERSION); + + /* install handler to close stuff off on exit and log notice */ + atexit(exithandler); + + /* write pidfile */ + write_pidfile(NSLCD_PIDFILE); + + /* create socket */ + nslcd_serversocket=open_socket(); + +#ifdef HAVE_SETGROUPS + /* drop all supplemental groups */ + if (setgroups(0,NULL)<0) + { + log_log(LOG_WARNING,"cannot setgroups(0,NULL) (ignored): %s",strerror(errno)); + } + else + { + log_log(LOG_DEBUG,"debug: setgroups(0,NULL) done"); + } +#else /* HAVE_SETGROUPS */ + log_log(LOG_DEBUG,"debug: setgroups() not available"); +#endif /* not HAVE_SETGROUPS */ + +#ifdef USE_CAPABILITIES + /* if we are using capbilities, set them to be kept + across setuid() calls so we can limit them later on */ + if (prctl(PR_SET_KEEPCAPS,1)) + { + log_log(LOG_ERR,"cannot prctl(PR_SET_KEEPCAPS,1): %s",strerror(errno)); + exit(1); + } + log_log(LOG_DEBUG,"debug: prctl(PR_SET_KEEPCAPS,1) done"); + /* dump the current capabilities */ + caps=cap_get_proc(); + log_log(LOG_DEBUG,"debug: current capabilities: %s",cap_to_text(caps,NULL)); + cap_free(caps); +#endif /* USE_CAPABILITIES */ + + /* change to nslcd gid */ + if (mygid!=((gid_t)-1)) + { + if (setgid(mygid)!=0) + { + log_log(LOG_ERR,"cannot setgid(%d): %s",(int)mygid,strerror(errno)); + exit(1); + } + log_log(LOG_DEBUG,"debug: setgid(%d) done",mygid); + } + + /* change to nslcd uid */ + if (myuid!=((uid_t)-1)) + { + if (setuid(myuid)!=0) + { + log_log(LOG_ERR,"cannot setuid(%d): %s",(int)myuid,strerror(errno)); + exit(1); + } + log_log(LOG_DEBUG,"debug: setuid(%d) done",myuid); + } + +#ifdef USE_CAPABILITIES + /* limit the capabilities */ + if (cap_set_proc(mycapabilities)!=0) + { + log_log(LOG_ERR,"cannot cap_set_proc(%s): %s",cap_to_text(mycapabilities,NULL),strerror(errno)); + exit(1); + } + log_log(LOG_DEBUG,"debug: cap_set_proc(%2) done",cap_to_text(mycapabilities,NULL)); + /* we no longer need this so we should free it */ + cap_free(mycapabilities); + /* dump the current capabilities */ + caps=cap_get_proc(); + log_log(LOG_DEBUG,"debug: current capabilities: %s",cap_to_text(caps,NULL)); + cap_free(caps); +#endif /* USE_CAPABILITIES */ + + /* install signalhandlers for some signals */ + install_sighandler(SIGHUP, sigexit_handler); + install_sighandler(SIGINT, sigexit_handler); + install_sighandler(SIGQUIT,sigexit_handler); + install_sighandler(SIGABRT,sigexit_handler); + install_sighandler(SIGPIPE,SIG_IGN); + install_sighandler(SIGTERM,sigexit_handler); + install_sighandler(SIGUSR1,sigexit_handler); + install_sighandler(SIGUSR2,sigexit_handler); + /* TODO: install signal handlers for reloading configuration */ + + log_log(LOG_INFO,"accepting connections"); + + /* start worker threads */ + for (i=0;i<NUM_THREADS;i++) + { + if (pthread_create(&nslcd_threads[i],NULL,worker,NULL)) + { + log_log(LOG_ERR,"unable to start worker thread %d: %s",i,strerror(errno)); + exit(1); + } + } + + /* wait for all threads to die */ + for (i=0;i<NUM_THREADS;i++) + { + if (pthread_join(nslcd_threads[i],NULL)) + { + log_log(LOG_ERR,"unable to wait for worker thread %d: %s",i,strerror(errno)); + exit(1); + } + } + + /* print something about received signals */ + if (nslcd_exitsignal!=0) + { + log_log(LOG_INFO,"caught signal %s (%d), shutting down", + signame(nslcd_exitsignal),nslcd_exitsignal); + } + + return 1; +} diff --git a/nslcd/pagectrl.c b/nslcd/pagectrl.c new file mode 100644 index 0000000..ca439dd --- /dev/null +++ b/nslcd/pagectrl.c @@ -0,0 +1,224 @@ +/* + pagectrl.c - provide a replacement ldap_create_page_control() function. + This file was part of the nss_ldap library which has been + forked into the nss-ldapd library. + + Copyright (C) 2002 Max Caines + This software is not subject to any license of the University + of Wolverhampton. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +/* TODO: move this to compat/ and add it only when needed */ +/* Note: this is only used in ldap-nss.c */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <lber.h> +#include <ldap.h> + +#include "pagectrl.h" + +#ifndef LDAP_CONTROL_PAGE_OID +#define LDAP_CONTROL_PAGE_OID "1.2.840.113556.1.4.319" +#endif + +#ifndef HAVE_LDAP_CREATE_CONTROL +#error LDAP client library does not support ldap_create_control() +#endif + +#ifndef HAVE_LDAP_CREATE_PAGE_CONTROL +/*--- + ldap_create_page_control + + Create and encode the Paged Results control. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + pagesize (IN) The number of entries to return in each page + + cookiep (IN) Pointer to a berVal structure that the server uses to + determine the current location in the + result set (opaque). Set to NULL the + first time. + + iscritical (IN) Is this control critical to the search? + + ctrlp (OUT) A result parameter that will be assigned the address + of an LDAPControl structure that contains the + PagedResult control created by this function. + The memory occupied by the LDAPControl structure + SHOULD be freed when it is no longer in use by + calling ldap_control_free(). + + + Ber encoding + + PageResult ::= SEQUENCE { + pageSize INTEGER + cookie OCTET STRING } + + + Note: The first time the Page control is created, the cookie + should be set to a zero-length string. The cookie obtained + from calling ldap_parse_page_control() should be used as + the cookie in the next ldap_create_page_control call. + + ---*/ + +int +ldap_create_page_control (LDAP * ld, + unsigned long pagesize, + struct berval *cookiep, + int iscritical, LDAPControl ** ctrlp) +{ + ber_tag_t tag; + BerElement *ber; + BerElement *ldap_alloc_ber_with_options (LDAP * ld); + int rc; + + if ((ld == NULL) || (ctrlp == NULL)) + { + return (LDAP_PARAM_ERROR); + } + + if ((ber = ldap_alloc_ber_with_options (ld)) == NULL) + { + return (LDAP_NO_MEMORY); + } + + tag = ber_printf (ber, "{i", pagesize); + if (tag == LBER_ERROR) + goto exit; + + if (cookiep == NULL) + tag = ber_printf (ber, "o", "", 0); + else + tag = ber_printf (ber, "O", cookiep); + if (tag == LBER_ERROR) + goto exit; + + tag = ber_printf (ber, /*{ */ "N}"); + if (tag == LBER_ERROR) + goto exit; + + rc = ldap_create_control (LDAP_CONTROL_PAGE_OID, ber, iscritical, ctrlp); + + ber_free (ber, 1); + return (rc); + +exit: + ber_free (ber, 1); + return (LDAP_ENCODING_ERROR); +} +#endif /* HAVE_LDAP_CREATE_PAGE_CONTROL */ + +#ifndef HAVE_LDAP_PARSE_PAGE_CONTROL +/*--- + ldap_parse_page_control + + Decode the Virtual List View control return information. + + ld (IN) An LDAP session handle. + + ctrls (IN) The address of a NULL-terminated array of + LDAPControl structures, typically obtained + by a call to ldap_parse_result(). + + list_countp (OUT) This result parameter is filled in with the number + of entries returned in this page + + cookiep (OUT) This result parameter is filled in with the address + of a struct berval that contains the server- + generated cookie. + The returned cookie SHOULD be used in the next call + to create a Page sort control. The struct berval + returned SHOULD be disposed of by calling ber_bvfree() + when it is no longer needed. + +---*/ +int +ldap_parse_page_control (LDAP * ld, + LDAPControl ** ctrls, + unsigned long *list_countp, struct berval **cookiep) +{ + BerElement *ber; + LDAPControl *pControl; + int i; + unsigned long count; + ber_tag_t tag; + + if (cookiep) + { + *cookiep = NULL; /* Make sure we return a NULL if error occurs. */ + } + + if (ld == NULL) + { + return (LDAP_PARAM_ERROR); + } + + if (ctrls == NULL) + { + return (LDAP_CONTROL_NOT_FOUND); + } + + /* Search the list of control responses for a Page control. */ + for (i = 0; ctrls[i]; i++) + { + pControl = ctrls[i]; + if (!strcmp (LDAP_CONTROL_PAGE_OID, pControl->ldctl_oid)) + goto foundPageControl; + } + + /* No page control was found. */ + return (LDAP_CONTROL_NOT_FOUND); + +foundPageControl: + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init (&pControl->ldctl_value); + + if (ber == NULL) + { + return (LDAP_NO_MEMORY); + } + + /* Extract the data returned in the control. */ + tag = ber_scanf (ber, "{iO" /*} */ , &count, cookiep); + + if (tag == LBER_ERROR) + { + ber_free (ber, 1); + return (LDAP_DECODING_ERROR); + } + + ber_free (ber, 1); + + /* Return data to the caller for items that were requested. */ + if (list_countp) + { + *list_countp = count; + } + + return (LDAP_SUCCESS); +} +#endif /* HAVE_LDAP_PARSE_PAGE_CONTROL */ diff --git a/nslcd/pagectrl.h b/nslcd/pagectrl.h new file mode 100644 index 0000000..c678ca9 --- /dev/null +++ b/nslcd/pagectrl.h @@ -0,0 +1,45 @@ +/* + pagectrl.h - provide a replacement ldap_create_page_control() function. + This file was part of the nss_ldap library which has been + forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#ifndef _LDAP_NSS_LDAP_PAGECTRL_H +#define _LDAP_NSS_LDAP_PAGECTRL_H + +#ifndef HAVE_LDAP_CREATE_PAGE_CONTROL +int +ldap_create_page_control( LDAP *ld, + unsigned long pagesize, + struct berval *cookiep, + int iscritical, + LDAPControl **ctrlp ); +#endif /* HAVE_LDAP_CREATE_PAGE_CONTROL */ + +#ifndef HAVE_LDAP_PARSE_PAGE_CONTROL +int +ldap_parse_page_control( + LDAP *ld, + LDAPControl **ctrls, + unsigned long *list_countp, + struct berval **cookiep ); +#endif /* HAVE_LDAP_PARSE_PAGE_CONTROL */ + +#endif /* _LDAP_NSS_LDAP_UTIL_H */ diff --git a/nslcd/passwd.c b/nslcd/passwd.c new file mode 100644 index 0000000..abdb104 --- /dev/null +++ b/nslcd/passwd.c @@ -0,0 +1,273 @@ +/* + passwd.c - password entry lookup routines + This file was part of the nss_ldap library (as ldap-pwd.c) + which has been forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/param.h> +#include <string.h> +#include <pwd.h> +#include <errno.h> +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif +#include <stdio.h> + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +#ifndef UID_NOBODY +#define UID_NOBODY (-2) +#endif + +#ifndef GID_NOBODY +#define GID_NOBODY UID_NOBODY +#endif + + +static inline enum nss_status _nss_ldap_assign_emptystring( + char **valptr, char **buffer, size_t * buflen) +{ + if (*buflen < 2) + return NSS_STATUS_TRYAGAIN; + + *valptr = *buffer; + + **valptr = '\0'; + + (*buffer)++; + (*buflen)--; + + return NSS_STATUS_SUCCESS; +} + +static enum nss_status _nss_ldap_parse_pw (LDAPMessage * e, + struct ldap_state * pvt, + void *result, char *buffer, size_t buflen) +{ + struct passwd *pw = (struct passwd *) result; + char *uid, *gid; + enum nss_status stat; + char tmpbuf[ sizeof( uid_t ) * 8 / 3 + 2 ]; + size_t tmplen; + char *tmp; + + tmpbuf[ sizeof(tmpbuf) - 1 ] = '\0'; + + if (_nss_ldap_oc_check (e, "shadowAccount") == NSS_STATUS_SUCCESS) + { + /* don't include password for shadowAccount */ + if (buflen < 3) + return NSS_STATUS_TRYAGAIN; + + pw->pw_passwd = buffer; + strcpy (buffer, "x"); + buffer += 2; + buflen -= 2; + } + else + { + stat = + _nss_ldap_assign_userpassword (e, ATM (LM_PASSWD, userPassword), + &pw->pw_passwd, &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + } + + stat = + _nss_ldap_assign_attrval (e, ATM (LM_PASSWD, uid), &pw->pw_name, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + tmp = tmpbuf; + tmplen = sizeof (tmpbuf) - 1; + stat = + _nss_ldap_assign_attrval (e, AT (uidNumber), &uid, &tmp, &tmplen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + pw->pw_uid = (*uid == '\0') ? UID_NOBODY : (uid_t) atol (uid); + + tmp = tmpbuf; + tmplen = sizeof (tmpbuf) - 1; + stat = + _nss_ldap_assign_attrval (e, ATM (LM_PASSWD, gidNumber), &gid, &tmp, + &tmplen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + pw->pw_gid = (*gid == '\0') ? GID_NOBODY : (gid_t) atol (gid); + + stat = + _nss_ldap_assign_attrval (e, AT (gecos), &pw->pw_gecos, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + { + pw->pw_gecos = NULL; + stat = + _nss_ldap_assign_attrval (e, ATM (LM_PASSWD, cn), &pw->pw_gecos, + &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + } + + stat = + _nss_ldap_assign_attrval (e, AT (homeDirectory), &pw->pw_dir, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + (void) _nss_ldap_assign_emptystring (&pw->pw_dir, &buffer, &buflen); + + stat = + _nss_ldap_assign_attrval (e, AT (loginShell), &pw->pw_shell, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + (void) _nss_ldap_assign_emptystring (&pw->pw_shell, &buffer, &buflen); + + return NSS_STATUS_SUCCESS; +} + +/* macros for expanding the NSLCD_PASSWD macro */ +#define NSLCD_STRING(field) WRITE_STRING(fp,field) +#define NSLCD_TYPE(field,type) WRITE_TYPE(fp,field,type) +#define PASSWD_NAME result.pw_name +#define PASSWD_PASSWD result.pw_passwd +#define PASSWD_UID result.pw_uid +#define PASSWD_GID result.pw_gid +#define PASSWD_GECOS result.pw_gecos +#define PASSWD_DIR result.pw_dir +#define PASSWD_SHELL result.pw_shell + +/* the caller should take care of opening and closing the stream */ +int nslcd_passwd_byname(FILE *fp) +{ + int32_t tmpint32; + char *name; + /* these are here for now until we rewrite the LDAP code */ + struct passwd result; + char buffer[1024]; + int errnop; + int retv; + struct ldap_args a; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* FIXME: free() this buffer somewhere */ + /* log call */ + log_log(LOG_DEBUG,"nslcd_passwd_byname(%s)",name); + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getpwnam,LM_PASSWD,_nss_ldap_parse_pw)); + /* no more need for this string */ + free(name); + /* write the response */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_PASSWD_BYNAME); + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + { + NSLCD_PASSWD; + } + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_passwd_byuid(FILE *fp) +{ + int32_t tmpint32; + uid_t uid; + /* these are here for now until we rewrite the LDAP code */ + struct passwd result; + char buffer[1024]; + int errnop; + int retv; + struct ldap_args a; + /* read request parameters */ + READ_TYPE(fp,uid,uid_t); + /* log call */ + log_log(LOG_DEBUG,"nslcd_passwd_byuid(%d)",(int)uid); + /* do the LDAP request */ + LA_INIT(a); + LA_NUMBER(a)=uid; + LA_TYPE(a)=LA_TYPE_NUMBER; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getpwuid,LM_PASSWD,_nss_ldap_parse_pw)); + /* write the response */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_PASSWD_BYUID); + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + { + NSLCD_PASSWD; + } + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_passwd_all(FILE *fp) +{ + int32_t tmpint32; + /* these are here for now until we rewrite the LDAP code */ + struct ent_context *pw_context = NULL; + struct passwd result; + char buffer[1024]; + int errnop; + int retv; + /* log call */ + log_log(LOG_DEBUG,"nslcd_passwd_all()"); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_PASSWD_ALL); + /* initialize context */ + if (_nss_ldap_ent_context_init(&pw_context)==NULL) + return -1; + /* go over results */ + while ((retv=nss2nslcd(_nss_ldap_getent(&pw_context,&result,buffer,1024,&errnop,_nss_ldap_filt_getpwent,LM_PASSWD,_nss_ldap_parse_pw)))==NSLCD_RESULT_SUCCESS) + { + /* write the result */ + WRITE_INT32(fp,retv); + NSLCD_PASSWD; + } + /* write the final result code */ + WRITE_INT32(fp,retv); + WRITE_FLUSH(fp); + /* FIXME: if some statement returns what happens to the context? */ + _nss_ldap_enter(); \ + _nss_ldap_ent_context_release(pw_context); \ + _nss_ldap_leave(); \ + /* we're done */ + return 0; +} diff --git a/nslcd/protocol.c b/nslcd/protocol.c new file mode 100644 index 0000000..71ce52d --- /dev/null +++ b/nslcd/protocol.c @@ -0,0 +1,198 @@ +/* + protocol.c - network address entry lookup routines + This file was part of the nss_ldap library (as ldap-proto.c) + which has been forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +/* + Determine the canonical name of the RPC with _nss_ldap_getrdnvalue(), + and assign any values of "cn" which do NOT match this canonical name + as aliases. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <errno.h> +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +static enum nss_status _nss_ldap_parse_proto (LDAPMessage *e, + struct ldap_state *pvt, + void *result, char *buffer, size_t buflen) +{ + + struct protoent *proto = (struct protoent *) result; + char *number; + enum nss_status stat; + + stat = + _nss_ldap_getrdnvalue (e, ATM (LM_PROTOCOLS, cn), &proto->p_name, + &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = + _nss_ldap_assign_attrval (e, AT (ipProtocolNumber), &number, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + proto->p_proto = atoi (number); + + stat = + _nss_ldap_assign_attrvals (e, ATM (LM_PROTOCOLS, cn), proto->p_name, + &proto->p_aliases, &buffer, &buflen, NULL); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + return NSS_STATUS_SUCCESS; +} + +/* macros for expanding the NSLCD_PROTOCOL macro */ +#define NSLCD_STRING(field) WRITE_STRING(fp,field) +#define NSLCD_STRINGLIST(field) WRITE_STRINGLIST_NULLTERM(fp,field) +#define NSLCD_INT32(field) WRITE_INT32(fp,field) +#define PROTOCOL_NAME result.p_name +#define PROTOCOL_ALIASES result.p_aliases +#define PROTOCOL_NUMBER result.p_proto + +int nslcd_protocol_byname(FILE *fp) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + char *name; + struct ldap_args a; + /* these are here for now until we rewrite the LDAP code */ + struct protoent result; + char buffer[1024]; + int errnop; + int retv; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* log call */ + log_log(LOG_DEBUG,"nslcd_protocol_byname(%s)",name); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_PROTOCOL_BYNAME); + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getprotobyname,LM_PROTOCOLS,_nss_ldap_parse_proto)); + /* no more need for this string */ + free(name); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + { + NSLCD_PROTOCOL; + } + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_protocol_bynumber(FILE *fp) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + int protocol; + struct ldap_args a; + /* these are here for now until we rewrite the LDAP code */ + struct protoent result; + char buffer[1024]; + int errnop; + int retv; + /* read request parameters */ + READ_INT32(fp,protocol); + /* log call */ + log_log(LOG_DEBUG,"nslcd_protocol_bynumber(%d)",protocol); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_PROTOCOL_BYNUMBER); + /* do the LDAP request */ + LA_INIT(a); + LA_NUMBER(a)=protocol; + LA_TYPE(a)=LA_TYPE_NUMBER; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getprotobynumber,LM_PROTOCOLS,_nss_ldap_parse_proto)); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + { + NSLCD_PROTOCOL; + } + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_protocol_all(FILE *fp) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + static struct ent_context *protocol_context; + /* these are here for now until we rewrite the LDAP code */ + struct protoent result; + char buffer[1024]; + int errnop; + int retv; + /* log call */ + log_log(LOG_DEBUG,"nslcd_protocol_all()"); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_PROTOCOL_ALL); + /* initialize context */ + if (_nss_ldap_ent_context_init(&protocol_context)==NULL) + return -1; + /* loop over all results */ + while ((retv=nss2nslcd(_nss_ldap_getent(&protocol_context,&result,buffer,1024,&errnop,_nss_ldap_filt_getprotoent,LM_PROTOCOLS,_nss_ldap_parse_proto)))==NSLCD_RESULT_SUCCESS) + { + /* write the result code */ + WRITE_INT32(fp,retv); + /* write the entry */ + NSLCD_PROTOCOL; + } + /* write the final result code */ + WRITE_INT32(fp,retv); + WRITE_FLUSH(fp); + /* FIXME: if a previous call returns what happens to the context? */ + _nss_ldap_enter(); + _nss_ldap_ent_context_release(protocol_context); + _nss_ldap_leave(); + /* we're done */ + return 0; +} diff --git a/nslcd/resolve.c b/nslcd/resolve.c new file mode 100644 index 0000000..470d33d --- /dev/null +++ b/nslcd/resolve.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Hvgskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Kungliga Tekniska + * Hvgskolan and its contributors. + * + * 4. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <string.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#include <resolv.h> + +#include "resolve.h" + +#if defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) + +#define DECL(X) {#X, T_##X} + +static struct stot +{ + char *name; + int type; +} +stot[] = +{ + DECL (A), + DECL (NS), + DECL (CNAME), DECL (PTR), DECL (MX), DECL (TXT), DECL (AFSDB), DECL (SRV), + { + NULL, 0} +}; + +static int +string_to_type (const char *name) +{ + struct stot *p = stot; + for (p = stot; p->name; p++) + if (strcasecmp (name, p->name) == 0) + return p->type; + return -1; +} + +void +dns_free_data (struct dns_reply *r) +{ + struct resource_record *rr; + if (r->q.domain) + free (r->q.domain); + for (rr = r->head; rr;) + { + struct resource_record *tmp = rr; + if (rr->domain) + free (rr->domain); + if (rr->u.data) + free (rr->u.data); + rr = rr->next; + free (tmp); + } + free (r); +} + +static struct dns_reply * +parse_reply (unsigned char *data, int len) +{ + unsigned char *p; + char host[128]; + int status; + int query, response; + + struct dns_reply *r; + struct resource_record **rr; + + r = (struct dns_reply *) malloc (sizeof (struct dns_reply)); + memset (r, 0, sizeof (struct dns_reply)); + r->q.domain = NULL; + + p = data; + memcpy (&r->h, p, sizeof (HEADER)); + p += sizeof (HEADER); + for (query = 0; query < ntohs(r->h.qdcount); query++) + { + status = dn_expand (data, data + len, p, host, sizeof (host)); + if (status < 0) + { + dns_free_data (r); + return NULL; + } + p += status; + if (p + 4 > data + len) + { + dns_free_data (r); + return NULL; + } + if (r->q.domain == NULL) + { + r->q.domain = strdup (host); + r->q.type = (p[0] << 8 | p[1]); + r->q.class = (p[2] << 8 | p[3]); + } + p += 4; + } + rr = &r->head; + for (response = 0; (response < ntohs(r->h.ancount)) && (p < data + len); response++) + { + unsigned int type, class, ttl, size; + status = dn_expand (data, data + len, p, host, sizeof (host)); + if (status < 0) + { + dns_free_data (r); + return NULL; + } + p += status; + if (p + 10 > data + len) + { + dns_free_data (r); + return NULL; + } + type = (p[0] << 8) | p[1]; + p += 2; + class = (p[0] << 8) | p[1]; + p += 2; + ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + p += 4; + size = (p[0] << 8) | p[1]; + p += 2; + if (p + size > data + len) + { + dns_free_data (r); + return NULL; + } + *rr = (struct resource_record *) calloc (1, + sizeof (struct + resource_record)); + (*rr)->domain = strdup (host); + (*rr)->type = type; + (*rr)->class = class; + (*rr)->ttl = ttl; + (*rr)->size = size; + switch (type) + { + case T_NS: + case T_CNAME: + case T_PTR: + status = dn_expand (data, data + len, p, host, sizeof (host)); + if (status < 0) + { + dns_free_data (r); + return NULL; + } + (*rr)->u.txt = strdup (host); + break; + case T_MX: + case T_AFSDB: + { + if (p + 2 > data + len) + { + dns_free_data (r); + return NULL; + } + status = dn_expand (data, data + len, p + 2, host, sizeof (host)); + if (status < 0) + { + dns_free_data (r); + return NULL; + } + + (*rr)->u.mx = + (struct mx_record *) malloc (sizeof (struct mx_record) + + strlen (host)); + (*rr)->u.mx->preference = (p[0] << 8) | p[1]; + strcpy ((*rr)->u.mx->domain, host); + break; + } + case T_SRV: + { + if (p + 6 > data + len) + { + dns_free_data (r); + return NULL; + } + status = dn_expand (data, data + len, p + 6, host, sizeof (host)); + if (status < 0) + { + dns_free_data (r); + return NULL; + } + (*rr)->u.srv = + (struct srv_record *) malloc (sizeof (struct srv_record) + + strlen (host)); + (*rr)->u.srv->priority = (p[0] << 8) | p[1]; + (*rr)->u.srv->weight = (p[2] << 8) | p[3]; + (*rr)->u.srv->port = (p[4] << 8) | p[5]; + strcpy ((*rr)->u.srv->target, host); + break; + } + case T_TXT: + { + if (p + *p > data + len) + { + dns_free_data (r); + return NULL; + } + (*rr)->u.txt = (char *) malloc (size + 1); + strncpy ((*rr)->u.txt, (char *) p + 1, *p); + (*rr)->u.txt[*p] = 0; + break; + } + + default: + (*rr)->u.data = (unsigned char *) malloc (size); + memcpy ((*rr)->u.data, p, size); + } + p += size; + rr = &(*rr)->next; + } + *rr = NULL; + return r; +} + +struct dns_reply * +dns_lookup (const char *domain, const char *type_name) +{ + unsigned char *reply = NULL; + int len, rlen; + int type; + struct dns_reply *r = NULL; + + type = string_to_type (type_name); + rlen = 1024; + reply = malloc(rlen); + do + { + len = res_search (domain, C_IN, type, reply, rlen); + if ((len == -1) || (len < rlen)) + { + break; + } + reply = realloc (reply, len + 1024); + rlen = len + 1024; + } + while (1); + if (len >= 0) + r = parse_reply (reply, len); + free(reply); + return r; +} + +#else /* defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */ + +struct dns_reply * +dns_lookup (const char *domain, const char *type_name) +{ + return NULL; +} + +void +dns_free_data (struct dns_reply *r) +{ +} + +#endif /* not ( defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) ) */ + +#ifdef TEST + +int +main (int argc, char **argv) +{ + struct dns_reply *r; + struct resource_record *rr; + r = dns_lookup (argv[1], argv[2]); + if (r == NULL) + { + printf ("No reply.\n"); + return 1; + } + for (rr = r->head; rr; rr = rr->next) + { + printf ("%s %s %d ", rr->domain, type_to_string (rr->type), rr->ttl); + switch (rr->type) + { + case T_NS: + printf ("%s\n", (char *) rr->data); + break; + case T_A: + printf ("%d.%d.%d.%d\n", + ((unsigned char *) rr->data)[0], + ((unsigned char *) rr->data)[1], + ((unsigned char *) rr->data)[2], + ((unsigned char *) rr->data)[3]); + break; + case T_MX: + case T_AFSDB: + { + struct mx_record *mx = (struct mx_record *) rr->data; + printf ("%d %s\n", mx->preference, mx->domain); + break; + } + case T_SRV: + { + struct srv_record *srv = (struct srv_record *) rr->data; + printf ("%d %d %d %s\n", srv->priority, srv->weight, + srv->port, srv->target); + break; + } + default: + printf ("\n"); + break; + } + } + + return 0; +} +#endif /* TEST */ diff --git a/nslcd/resolve.h b/nslcd/resolve.h new file mode 100644 index 0000000..62b2574 --- /dev/null +++ b/nslcd/resolve.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Hvgskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Kungliga Tekniska + * Hvgskolan and its contributors. + * + * 4. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* THIS IS NOT (yet) A PUBLIC INTERFACE */ + +#ifndef __RESOLVE_H__ +#define __RESOLVE_H__ + +/* when would this *not* be defined? */ +#define HAVE_ARPA_NAMESER_H + +/* We use these, but they are not always present in <arpa/nameser.h> */ + +#ifndef T_TXT +#define T_TXT 16 +#endif +#ifndef T_AFSDB +#define T_AFSDB 18 +#endif +#ifndef T_SRV +#define T_SRV 33 +#endif + +struct dns_query +{ + char *domain; + unsigned type; + unsigned class; +}; + +struct mx_record +{ + unsigned preference; + char domain[1]; +}; + +struct srv_record +{ + unsigned priority; + unsigned weight; + unsigned port; + char target[1]; +}; + +struct resource_record +{ + char *domain; + unsigned type; + unsigned class; + unsigned ttl; + unsigned size; + union + { + void *data; + struct mx_record *mx; + struct mx_record *afsdb; /* mx and afsdb are identical */ + struct srv_record *srv; + struct in_addr *a; + char *txt; + } + u; + struct resource_record *next; +}; + +#ifndef HAVE_ARPA_NAMESER_H /* XXX */ +typedef int HEADER; /* will never be used */ +#endif + +struct dns_reply +{ + HEADER h; + struct dns_query q; + struct resource_record *head; +}; + +#define dns_lookup _nss_ldap_dns_lookup +#define dns_free_data _nss_ldap_dns_free_data + +struct dns_reply *dns_lookup (const char *, const char *); + +void dns_free_data (struct dns_reply *r); + +#endif /* __RESOLVE_H__ */ diff --git a/nslcd/rpc.c b/nslcd/rpc.c new file mode 100644 index 0000000..b58cca7 --- /dev/null +++ b/nslcd/rpc.c @@ -0,0 +1,206 @@ +/* + rpc.c - rpc name lookup routines + This file was part of the nss_ldap library (as ldap-rpc.c) which + has been forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +/* + Determine the canonical name of the RPC with _nss_ldap_getrdnvalue(), + and assign any values of "cn" which do NOT match this canonical name + as aliases. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#ifdef HAVE_RPC_RPCENT_H +#include <rpc/rpcent.h> +#else +#include <netdb.h> +#endif +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +/* macros for expanding the NSLCD_RPC macro */ +#define NSLCD_STRING(field) WRITE_STRING(fp,field) +#define NSLCD_STRINGLIST(field) WRITE_STRINGLIST_NULLTERM(fp,field) +#define NSLCD_INT32(field) WRITE_INT32(fp,field) +#define RPC_NAME result->r_name +#define RPC_ALIASES result->r_aliases +#define RPC_NUMBER result->r_number + +/* write a single rpc entry to the stream */ +static int write_rpcent(FILE *fp,struct rpcent *result) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + NSLCD_RPC; + return 0; +} + +static enum nss_status _nss_ldap_parse_rpc (LDAPMessage * e, + struct ldap_state * pvt, + void *result, char *buffer, size_t buflen) +{ + + struct rpcent *rpc = (struct rpcent *) result; + char *number; + enum nss_status stat; + + stat = + _nss_ldap_getrdnvalue (e, ATM (LM_RPC, cn), &rpc->r_name, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = + _nss_ldap_assign_attrval (e, AT (oncRpcNumber), &number, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + rpc->r_number = atol (number); + + stat = + _nss_ldap_assign_attrvals (e, ATM (LM_RPC, cn), rpc->r_name, + &rpc->r_aliases, &buffer, &buflen, NULL); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + return NSS_STATUS_SUCCESS; +} + +int nslcd_rpc_byname(FILE *fp) +{ + int32_t tmpint32; + char *name; + struct ldap_args a; + /* these are here for now until we rewrite the LDAP code */ + struct rpcent result; + char buffer[1024]; + int errnop; + int retv; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* log call */ + log_log(LOG_DEBUG,"nslcd_rpc_byname(%s)",name); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_RPC_BYNAME); + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getrpcbyname,LM_RPC,_nss_ldap_parse_rpc)); + /* no more need for this string */ + free(name); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + write_rpcent(fp,&result); + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_rpc_bynumber(FILE *fp) +{ + int32_t tmpint32; + int number; + struct ldap_args a; + /* these are here for now until we rewrite the LDAP code */ + struct rpcent result; + char buffer[1024]; + int errnop; + int retv; + /* read request parameters */ + READ_INT32(fp,number); + /* log call */ + log_log(LOG_DEBUG,"nslcd_rpc_bynumber(%d)",number); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_RPC_BYNUMBER); + /* do the LDAP request */ + LA_INIT(a); + LA_NUMBER(a)=number; + LA_TYPE(a)=LA_TYPE_NUMBER; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getrpcbynumber,LM_RPC,_nss_ldap_parse_rpc)); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + write_rpcent(fp,&result); + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_rpc_all(FILE *fp) +{ + int32_t tmpint32; + static struct ent_context *rpc_context; + /* these are here for now until we rewrite the LDAP code */ + struct rpcent result; + char buffer[1024]; + int errnop; + int retv; + /* log call */ + log_log(LOG_DEBUG,"nslcd_rpc_all()"); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_RPC_ALL); + /* initialize context */ + if (_nss_ldap_ent_context_init(&rpc_context)==NULL) + return -1; + /* loop over all results */ + while ((retv=nss2nslcd(_nss_ldap_getent(&rpc_context,&result,buffer,1024,&errnop,_nss_ldap_filt_getrpcent,LM_RPC,_nss_ldap_parse_rpc)))==NSLCD_RESULT_SUCCESS) + { + /* write the result code */ + WRITE_INT32(fp,retv); + /* write the entry */ + write_rpcent(fp,&result); + } + /* write the final result code */ + WRITE_INT32(fp,retv); + WRITE_FLUSH(fp); + /* FIXME: if a previous call returns what happens to the context? */ + _nss_ldap_enter(); + _nss_ldap_ent_context_release(rpc_context); + _nss_ldap_leave(); + /* we're done */ + return 0; +} diff --git a/nslcd/service.c b/nslcd/service.c new file mode 100644 index 0000000..7739b8e --- /dev/null +++ b/nslcd/service.c @@ -0,0 +1,302 @@ +/* + service.c - service entry lookup routines + This file was part of the nss_ldap library (as ldap-service.c) + which has been forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +/* + Determine the canonical name of the RPC with _nss_ldap_getrdnvalue(), + and assign any values of "cn" which do NOT match this canonical name + as aliases. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <netinet/in.h> +#include <errno.h> +#ifdef HAVE_SYS_BYTEORDER_H +#include <sys/byteorder.h> +#endif +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +/* macros for expanding the NSLCD_SERVICE macro */ +#define NSLCD_STRING(field) WRITE_STRING(fp,field) +#define NSLCD_STRINGLIST(field) WRITE_STRINGLIST_NULLTERM(fp,field) +#define NSLCD_INT32(field) WRITE_INT32(fp,field) +#define SERVICE_NAME result->s_name +#define SERVICE_ALIASES result->s_aliases +#define SERVICE_NUMBER htons(result->s_port) +#define SERVICE_PROTOCOL result->s_proto + +/* write a single host entry to the stream */ +static int write_servent(FILE *fp,struct servent *result) +{ + int32_t tmpint32,tmp2int32,tmp3int32; + NSLCD_SERVICE; + return 0; +} + +static enum nss_status _nss_ldap_parse_serv (LDAPMessage *e, + struct ldap_state *state, + void *result,char *buffer,size_t buflen) +{ + struct servent *service = (struct servent *)result; + char *port; + enum nss_status stat = NSS_STATUS_SUCCESS; + + /* this is complicated and ugly, because some git (me) specified that service + * entries should expand to two entities (or more) if they have multi-valued + * ipServiceProtocol fields. + */ + + if (state->ls_type == LS_TYPE_KEY) + { + if (state->ls_info.ls_key == NULL) + { + /* non-deterministic behaviour is ok */ + stat = + _nss_ldap_assign_attrval (e, AT (ipServiceProtocol), + &service->s_proto, &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS) + { + return stat; + } + } + else + { + register int len; + len = strlen (state->ls_info.ls_key); + if (buflen < (size_t) (len + 1)) + { + return NSS_STATUS_TRYAGAIN; + } + strncpy (buffer, state->ls_info.ls_key, len); + buffer[len] = '\0'; + service->s_proto = buffer; + buffer += len + 1; + buflen -= len + 1; + } + } + else + { + char **vals = _nss_ldap_get_values (e, AT (ipServiceProtocol)); + int len; + if (vals == NULL) + { + state->ls_info.ls_index = -1; + return NSS_STATUS_NOTFOUND; + } + + switch (state->ls_info.ls_index) + { + case 0: + /* last time. decrementing ls_index to -1 AND returning !NSS_STATUS_SUCCESS + will force this entry to be discarded. + */ + stat = NSS_STATUS_NOTFOUND; + break; + case -1: + /* first time */ + state->ls_info.ls_index = ldap_count_values (vals); + /* fall off to default ... */ + default: + len = strlen (vals[state->ls_info.ls_index - 1]); + if (buflen < (size_t) (len + 1)) + { + return NSS_STATUS_TRYAGAIN; + } + strncpy (buffer, vals[state->ls_info.ls_index - 1], len); + buffer[len] = '\0'; + service->s_proto = buffer; + buffer += len + 1; + buflen -= len + 1; + stat = NSS_STATUS_SUCCESS; + } + + ldap_value_free (vals); + state->ls_info.ls_index--; + } + + if (stat != NSS_STATUS_SUCCESS) + { + return stat; + } + + stat = + _nss_ldap_getrdnvalue (e, ATM (LM_SERVICES, cn), &service->s_name, + &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS) + { + return stat; + } + + stat = + _nss_ldap_assign_attrvals (e, ATM (LM_SERVICES, cn), service->s_name, + &service->s_aliases, &buffer, &buflen, NULL); + if (stat != NSS_STATUS_SUCCESS) + { + return stat; + } + + stat = + _nss_ldap_assign_attrval (e, AT (ipServicePort), &port, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + { + return stat; + } + + service->s_port = htons (atoi (port)); + + return NSS_STATUS_SUCCESS; +} + +int nslcd_service_byname(FILE *fp) +{ + int32_t tmpint32; + char *name,*protocol; + struct ldap_args a; + /* these are here for now until we rewrite the LDAP code */ + struct servent result; + char buffer[1024]; + int errnop; + int retv; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + READ_STRING_ALLOC(fp,protocol); + /* log call */ + log_log(LOG_DEBUG,"nslcd_service_byname(%s,%s)",name,protocol); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_SERVICE_BYNAME); + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=(strlen(protocol)==0)?LA_TYPE_STRING:LA_TYPE_STRING_AND_STRING; + LA_STRING2(a)=protocol; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop, + ((strlen(protocol)==0)?_nss_ldap_filt_getservbyname:_nss_ldap_filt_getservbynameproto), + LM_SERVICES,_nss_ldap_parse_serv)); + /* no more need for these strings */ + free(name); + free(protocol); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + write_servent(fp,&result); + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_service_bynumber(FILE *fp) +{ + int32_t tmpint32; + int number; + char *protocol; + struct ldap_args a; + /* these are here for now until we rewrite the LDAP code */ + struct servent result; + char buffer[1024]; + int errnop; + int retv; + /* read request parameters */ + READ_INT32(fp,number); + READ_STRING_ALLOC(fp,protocol); + /* log call */ + log_log(LOG_DEBUG,"nslcd_service_bynumber(%d,%s)",number,protocol); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_SERVICE_BYNUMBER); + /* do the LDAP request */ + LA_INIT(a); + LA_NUMBER(a)=number; + LA_TYPE(a)=(strlen(protocol)==0)?LA_TYPE_NUMBER:LA_TYPE_NUMBER_AND_STRING; + LA_STRING2(a)=protocol; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop, + ((strlen(protocol)==0)?_nss_ldap_filt_getservbyport:_nss_ldap_filt_getservbyportproto), + LM_SERVICES,_nss_ldap_parse_serv)); + /* no more need for this string */ + free(protocol); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + write_servent(fp,&result); + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_service_all(FILE *fp) +{ + int32_t tmpint32; + static struct ent_context *serv_context; + /* these are here for now until we rewrite the LDAP code */ + struct servent result; + char buffer[1024]; + int errnop; + int retv; + /* log call */ + log_log(LOG_DEBUG,"nslcd_service_all()"); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_SERVICE_ALL); + /* initialize context */ + if (_nss_ldap_ent_context_init(&serv_context)==NULL) + return -1; + /* loop over all results */ + while ((retv=nss2nslcd(_nss_ldap_getent(&serv_context,&result,buffer,1024,&errnop,_nss_ldap_filt_getservent,LM_SERVICES,_nss_ldap_parse_serv)))==NSLCD_RESULT_SUCCESS) + { + /* write the result code */ + WRITE_INT32(fp,retv); + /* write the entry */ + write_servent(fp,&result); + } + /* write the final result code */ + WRITE_INT32(fp,retv); + WRITE_FLUSH(fp); + /* FIXME: if a previous call returns what happens to the context? */ + _nss_ldap_enter(); + _nss_ldap_ent_context_release(serv_context); + _nss_ldap_leave(); + /* we're done */ + return 0; +} diff --git a/nslcd/shadow.c b/nslcd/shadow.c new file mode 100644 index 0000000..d1b22bd --- /dev/null +++ b/nslcd/shadow.c @@ -0,0 +1,189 @@ +/* + shadow.c - service entry lookup routines + This file was part of the nss_ldap library (as ldap-spwd.c) which + has been forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <shadow.h> +#include <errno.h> +#ifdef HAVE_PROT_H +#define _PROT_INCLUDED +#endif +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + +static enum nss_status _nss_ldap_parse_sp(LDAPMessage *e, + struct ldap_state *pvt, + void *result,char *buffer,size_t buflen) +{ + struct spwd *sp = (struct spwd *) result; + enum nss_status stat; + char *tmp = NULL; + + stat = + _nss_ldap_assign_userpassword (e, ATM (LM_SHADOW, userPassword), + &sp->sp_pwdp, &buffer, &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = + _nss_ldap_assign_attrval (e, ATM (LM_SHADOW, uid), &sp->sp_namp, &buffer, + &buflen); + if (stat != NSS_STATUS_SUCCESS) + return stat; + + stat = + _nss_ldap_assign_attrval (e, AT (shadowLastChange), &tmp, &buffer, + &buflen); + sp->sp_lstchg = (stat == NSS_STATUS_SUCCESS) ? _nss_ldap_shadow_date (tmp) : -1; + + stat = + _nss_ldap_assign_attrval (e, AT (shadowMax), &tmp, &buffer, &buflen); + sp->sp_max = (stat == NSS_STATUS_SUCCESS) ? atol (tmp) : -1; + + stat = + _nss_ldap_assign_attrval (e, AT (shadowMin), &tmp, &buffer, &buflen); + sp->sp_min = (stat == NSS_STATUS_SUCCESS) ? atol (tmp) : -1; + + stat = + _nss_ldap_assign_attrval (e, AT (shadowWarning), &tmp, &buffer, + &buflen); + sp->sp_warn = (stat == NSS_STATUS_SUCCESS) ? atol (tmp) : -1; + + stat = + _nss_ldap_assign_attrval (e, AT (shadowInactive), &tmp, &buffer, + &buflen); + sp->sp_inact = (stat == NSS_STATUS_SUCCESS) ? atol (tmp) : -1; + + stat = + _nss_ldap_assign_attrval (e, AT (shadowExpire), &tmp, &buffer, + &buflen); + sp->sp_expire = (stat == NSS_STATUS_SUCCESS) ? _nss_ldap_shadow_date (tmp) : -1; + + stat = + _nss_ldap_assign_attrval (e, AT (shadowFlag), &tmp, &buffer, &buflen); + sp->sp_flag = (stat == NSS_STATUS_SUCCESS) ? atol (tmp) : 0; + + _nss_ldap_shadow_handle_flag(sp); + + return NSS_STATUS_SUCCESS; +} + +/* macros for expanding the NSLCD_SHADOW macro */ +#define NSLCD_STRING(field) WRITE_STRING(fp,field) +#define NSLCD_INT32(field) WRITE_INT32(fp,field) +#define SHADOW_NAME result.sp_namp +#define SHADOW_PASSWD result.sp_pwdp +#define SHADOW_LASTCHANGE result.sp_lstchg +#define SHADOW_MINDAYS result.sp_min +#define SHADOW_MAXDAYS result.sp_max +#define SHADOW_WARN result.sp_warn +#define SHADOW_INACT result.sp_inact +#define SHADOW_EXPIRE result.sp_expire +#define SHADOW_FLAG result.sp_flag + +int nslcd_shadow_byname(FILE *fp) +{ + int32_t tmpint32; + char *name; + struct ldap_args a; + int retv; + struct spwd result; + char buffer[1024]; + int errnop; + /* read request parameters */ + READ_STRING_ALLOC(fp,name); + /* log call */ + log_log(LOG_DEBUG,"nslcd_shadow_byname(%s)",name); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_SHADOW_BYNAME); + /* do the LDAP request */ + LA_INIT(a); + LA_STRING(a)=name; + LA_TYPE(a)=LA_TYPE_STRING; + retv=nss2nslcd(_nss_ldap_getbyname(&a,&result,buffer,1024,&errnop,_nss_ldap_filt_getspnam,LM_SHADOW,_nss_ldap_parse_sp)); + /* no more need for this string */ + free(name); + /* write the response */ + WRITE_INT32(fp,retv); + if (retv==NSLCD_RESULT_SUCCESS) + { + NSLCD_SHADOW; + } + WRITE_FLUSH(fp); + /* we're done */ + return 0; +} + +int nslcd_shadow_all(FILE *fp) +{ + int32_t tmpint32; + static struct ent_context *shadow_context; + /* these are here for now until we rewrite the LDAP code */ + struct spwd result; + char buffer[1024]; + int errnop; + int retv; + /* log call */ + log_log(LOG_DEBUG,"nslcd_shadow_all()"); + /* write the response header */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_SHADOW_ALL); + /* initialize context */ + if (_nss_ldap_ent_context_init(&shadow_context)==NULL) + return -1; + /* loop over all results */ + while ((retv=nss2nslcd(_nss_ldap_getent(&shadow_context,&result,buffer,1024,&errnop,_nss_ldap_filt_getspent,LM_SHADOW,_nss_ldap_parse_sp)))==NSLCD_RESULT_SUCCESS) + { + /* write the result */ + WRITE_INT32(fp,retv); + NSLCD_SHADOW; + } + /* write the final result code */ + WRITE_INT32(fp,retv); + WRITE_FLUSH(fp); + /* FIXME: if a previous call returns what happens to the context? */ + _nss_ldap_enter(); + _nss_ldap_ent_context_release(shadow_context); + _nss_ldap_leave(); + /* we're done */ + return 0; +} diff --git a/nslcd/util.c b/nslcd/util.c new file mode 100644 index 0000000..57d6a10 --- /dev/null +++ b/nslcd/util.c @@ -0,0 +1,1669 @@ +/* + util.c - LDAP utility functions + This file was part of the nss_ldap library which has been + forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#include <stdlib.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <netdb.h> +#include <syslog.h> +#include <string.h> +#include <fcntl.h> +#include <assert.h> +#ifdef HAVE_LBER_H +#include <lber.h> +#endif +#ifdef HAVE_LDAP_H +#include <ldap.h> +#endif +#if defined(HAVE_THREAD_H) +#include <thread.h> +#elif defined(HAVE_PTHREAD_H) +#include <pthread.h> +#endif + +#include "ldap-nss.h" +#include "util.h" +#include "common.h" +#include "log.h" + + +#define NSS_LDAP_KEY_MAP_ATTRIBUTE "nss_map_attribute" +#define NSS_LDAP_KEY_MAP_OBJECTCLASS "nss_map_objectclass" +#define NSS_LDAP_KEY_SET_OVERRIDE "nss_override_attribute_value" +#define NSS_LDAP_KEY_SET_DEFAULT "nss_default_attribute_value" +#define NSS_LDAP_KEY_HOST "host" +#define NSS_LDAP_KEY_SCOPE "scope" +#define NSS_LDAP_KEY_BASE "base" +#define NSS_LDAP_KEY_PORT "port" +#define NSS_LDAP_KEY_BINDDN "binddn" +#define NSS_LDAP_KEY_BINDPW "bindpw" +#define NSS_LDAP_KEY_USESASL "use_sasl" +#define NSS_LDAP_KEY_SASLID "sasl_auth_id" +#define NSS_LDAP_KEY_DEREF "deref" +#define NSS_LDAP_KEY_ROOTBINDDN "rootbinddn" +#define NSS_LDAP_KEY_ROOTUSESASL "rootuse_sasl" +#define NSS_LDAP_KEY_ROOTSASLID "rootsasl_auth_id" +#define NSS_LDAP_KEY_LDAP_VERSION "ldap_version" +#define NSS_LDAP_KEY_TIMELIMIT "timelimit" +#define NSS_LDAP_KEY_BIND_TIMELIMIT "bind_timelimit" +#define NSS_LDAP_KEY_SSL "ssl" +#define NSS_LDAP_KEY_SSLPATH "sslpath" +#define NSS_LDAP_KEY_REFERRALS "referrals" +#define NSS_LDAP_KEY_RESTART "restart" +#define NSS_LDAP_KEY_URI "uri" +#define NSS_LDAP_KEY_IDLE_TIMELIMIT "idle_timelimit" +#define NSS_LDAP_KEY_RECONNECT_POLICY "bind_policy" +#define NSS_LDAP_KEY_SASL_SECPROPS "sasl_secprops" +#ifdef CONFIGURE_KRB5_CCNAME +#define NSS_LDAP_KEY_KRB5_CCNAME "krb5_ccname" +#endif /* CONFIGURE_KRB5_CCNAME */ +#define NSS_LDAP_KEY_LOGDIR "logdir" +#define NSS_LDAP_KEY_DEBUG "debug" +#define NSS_LDAP_KEY_PAGESIZE "pagesize" +#define NSS_LDAP_KEY_INITGROUPS "nss_initgroups" +#define NSS_LDAP_KEY_INITGROUPS_IGNOREUSERS "nss_initgroups_ignoreusers" + +/* more reconnect policy fine-tuning */ +#define NSS_LDAP_KEY_RECONNECT_TRIES "nss_reconnect_tries" +#define NSS_LDAP_KEY_RECONNECT_SLEEPTIME "nss_reconnect_sleeptime" +#define NSS_LDAP_KEY_RECONNECT_MAXSLEEPTIME "nss_reconnect_maxsleeptime" +#define NSS_LDAP_KEY_RECONNECT_MAXCONNTRIES "nss_reconnect_maxconntries" + +#define NSS_LDAP_KEY_PAGED_RESULTS "nss_paged_results" +#define NSS_LDAP_KEY_SCHEMA "nss_schema" +#define NSS_LDAP_KEY_SRV_DOMAIN "nss_srv_domain" +#define NSS_LDAP_KEY_CONNECT_POLICY "nss_connect_policy" + +/* + * Timeouts for reconnecting code. Similar to rebind + * logic in Darwin NetInfo. Some may find sleeping + * unacceptable, in which case you may wish to adjust + * the constants below. + */ +#define LDAP_NSS_TRIES 5 /* number of sleeping reconnect attempts */ +#define LDAP_NSS_SLEEPTIME 4 /* seconds to sleep; doubled until max */ +#define LDAP_NSS_MAXSLEEPTIME 64 /* maximum seconds to sleep */ +#define LDAP_NSS_MAXCONNTRIES 2 /* reconnect attempts before sleeping */ + +#define LDAP_PAGESIZE 1000 + + +static struct ldap_dictionary *do_alloc_dictionary(void); +static enum nss_status do_getrdnvalue (const char *dn, + const char *rdntype, + char **rval, char **buffer, + size_t * buflen); + +static enum nss_status do_parse_map_statement (struct ldap_config * cfg, + const char *statement, + enum ldap_map_type type); + +static enum nss_status do_searchdescriptorconfig (const char *key, + const char *value, + size_t valueLength, + struct ldap_service_search_descriptor + ** result, char **buffer, + size_t * buflen); + +static void *__cache = NULL; + +NSS_LDAP_DEFINE_LOCK (__cache_lock); + +#define cache_lock() NSS_LDAP_LOCK(__cache_lock) +#define cache_unlock() NSS_LDAP_UNLOCK(__cache_lock) + +static enum nss_status +dn2uid_cache_put (const char *dn, const char *uid) +{ + enum nss_status status; + struct ldap_datum key, val; + + cache_lock (); + + if (__cache == NULL) + { + __cache = (void *)do_alloc_dictionary(); + if (__cache == NULL) + { + cache_unlock (); + return NSS_STATUS_TRYAGAIN; + } + } + + key.data = (void *) dn; + key.size = strlen (dn); + val.data = (void *) uid; + val.size = strlen (uid); + + status = _nss_ldap_db_put (__cache, 0, &key, &val); + + cache_unlock (); + + return status; +} + +static enum nss_status +dn2uid_cache_get (const char *dn, char **uid, char **buffer, size_t * buflen) +{ + struct ldap_datum key, val; + enum nss_status status; + + cache_lock (); + + if (__cache == NULL) + { + cache_unlock (); + return NSS_STATUS_NOTFOUND; + } + + key.data = (void *) dn; + key.size = strlen (dn); + + status = _nss_ldap_db_get (__cache, 0, &key, &val); + if (status != NSS_STATUS_SUCCESS) + { + cache_unlock (); + return status; + } + + if (*buflen <= val.size) + { + cache_unlock (); + return NSS_STATUS_TRYAGAIN; + } + + *uid = *buffer; + memcpy (*uid, (char *) val.data, val.size); + (*uid)[val.size] = '\0'; + *buffer += val.size + 1; + *buflen -= val.size + 1; + + cache_unlock (); + return NSS_STATUS_SUCCESS; +} + +enum nss_status +_nss_ldap_dn2uid (const char *dn, char **uid, char **buffer, size_t * buflen, + int *pIsNestedGroup, LDAPMessage ** pRes) +{ + enum nss_status status; + + debug ("==> _nss_ldap_dn2uid"); + + *pIsNestedGroup = 0; + + status = dn2uid_cache_get (dn, uid, buffer, buflen); + if (status == NSS_STATUS_NOTFOUND) + { + const char *attrs[4]; + LDAPMessage *res; + + attrs[0] = ATM (LM_PASSWD, uid); + attrs[1] = ATM (LM_GROUP, uniqueMember); + attrs[2] = AT (objectClass); + attrs[3] = NULL; + + if (_nss_ldap_read (dn, attrs, &res) == NSS_STATUS_SUCCESS) + { + LDAPMessage *e = _nss_ldap_first_entry (res); + if (e != NULL) + { + if (_nss_ldap_oc_check (e, OC (posixGroup)) == NSS_STATUS_SUCCESS) + { + *pIsNestedGroup = 1; + *pRes = res; + debug ("<== _nss_ldap_dn2uid (nested group)"); + return NSS_STATUS_SUCCESS; + } + + status = + _nss_ldap_assign_attrval (e, ATM (LM_PASSWD, uid), uid, + buffer, buflen); + if (status == NSS_STATUS_SUCCESS) + dn2uid_cache_put (dn, *uid); + } + } + ldap_msgfree (res); + } + + debug ("<== _nss_ldap_dn2uid"); + + return status; +} + +enum nss_status +_nss_ldap_getrdnvalue (LDAPMessage * entry, + const char *rdntype, + char **rval, char **buffer, size_t * buflen) +{ + char *dn; + enum nss_status status; + + dn = _nss_ldap_get_dn (entry); + if (dn == NULL) + { + return NSS_STATUS_NOTFOUND; + } + + status = do_getrdnvalue (dn, rdntype, rval, buffer, buflen); +#ifdef HAVE_LDAP_MEMFREE + ldap_memfree (dn); +#else /* HAVE_LDAP_MEMFREE */ + free (dn); +#endif /* not HAVE_LDAP_MEMFREE */ + + /* + * If examining the DN failed, then pick the nominal first + * value of cn as the canonical name (recall that attributes + * are sets, not sequences) + */ + if (status == NSS_STATUS_NOTFOUND) + { + char **vals; + + vals = _nss_ldap_get_values (entry, rdntype); + + if (vals != NULL) + { + int rdnlen = strlen (*vals); + if (*buflen > rdnlen) + { + char *rdnvalue = *buffer; + strncpy (rdnvalue, *vals, rdnlen); + rdnvalue[rdnlen] = '\0'; + *buffer += rdnlen + 1; + *buflen -= rdnlen + 1; + *rval = rdnvalue; + status = NSS_STATUS_SUCCESS; + } + else + { + status = NSS_STATUS_TRYAGAIN; + } + ldap_value_free (vals); + } + } + + return status; +} + +int _nss_ldap_write_rndvalue(FILE *fp,LDAPMessage *entry,const char *rdntype) +{ + char *dn; + int status=456; + char **vals; + int32_t tmpint32; + char **exploded_dn; + char **exploded_rdn; + char rdnava[64]; + int rdnavalen; + int i; + /* log call */ + log_log(LOG_DEBUG,"_nss_ldap_write_rndvalue(%s)",rdntype); + /* get the dn from the entry */ + dn=_nss_ldap_get_dn(entry); + if (dn==NULL) + return NSLCD_RESULT_NOTFOUND; + /* append a `=' to the rdntype */ + snprintf(rdnava,sizeof(rdnava),"%s=",rdntype); + rdnavalen=strlen(rdnava); + /* explode dn */ + exploded_dn=ldap_explode_dn(dn,0); + if (exploded_dn!=NULL) + { + /* + * attempt to get the naming attribute's principal + * value by parsing the RDN. We need to support + * multivalued RDNs (as they're essentially mandated + * for services) + */ + exploded_rdn=ldap_explode_rdn(exploded_dn[0],0); + if (exploded_rdn!=NULL) + { + for (i=0;exploded_rdn[i]!=NULL;i++) + { + /* if the values begins with rndava */ + if (strncasecmp(exploded_rdn[i],rdnava,rdnavalen)==0) + { + /* FIXME: handle case where WRITE fails */ + WRITE_STRING(fp,exploded_rdn[i]+rdnavalen); + status=0; + break; + } + } + ldap_value_free(exploded_rdn); + } + ldap_value_free(exploded_dn); + } + ldap_memfree(dn); + /* + * If examining the DN failed, then pick the nominal first + * value of cn as the canonical name (recall that attributes + * are sets, not sequences) + */ + if (status==456) + { + vals=_nss_ldap_get_values(entry,rdntype); + if (vals!=NULL) + { + /* write the first entry */ + WRITE_STRING(fp,vals[0]); + status=NSS_STATUS_SUCCESS; + ldap_value_free(vals); + status=0; + } + } + return status; +} + +static enum nss_status +do_getrdnvalue (const char *dn, + const char *rdntype, + char **rval, char **buffer, size_t * buflen) +{ + char **exploded_dn; + char *rdnvalue = NULL; + char rdnava[64]; + int rdnlen = 0, rdnavalen; + + snprintf (rdnava, sizeof rdnava, "%s=", rdntype); + rdnavalen = strlen (rdnava); + + exploded_dn = ldap_explode_dn (dn, 0); + + if (exploded_dn != NULL) + { + /* + * attempt to get the naming attribute's principal + * value by parsing the RDN. We need to support + * multivalued RDNs (as they're essentially mandated + * for services) + */ +#ifdef HAVE_LDAP_EXPLODE_RDN + /* + * use ldap_explode_rdn() API, as it's cleaner than + * strtok(). This code has not been tested! + */ + char **p, **exploded_rdn; + + exploded_rdn = ldap_explode_rdn (*exploded_dn, 0); + if (exploded_rdn != NULL) + { + for (p = exploded_rdn; *p != NULL; p++) + { + if (strncasecmp (*p, rdnava, rdnavalen) == 0) + { + char *r = *p + rdnavalen; + + rdnlen = strlen (r); + if (*buflen <= rdnlen) + { + ldap_value_free (exploded_rdn); + ldap_value_free (exploded_dn); + return NSS_STATUS_TRYAGAIN; + } + rdnvalue = *buffer; + strncpy (rdnvalue, r, rdnlen); + break; + } + } + ldap_value_free (exploded_rdn); + } +#else /* HAVE_LDAP_EXPLODE_RDN */ + /* + * we don't have Netscape's ldap_explode_rdn() API, + * so we fudge it with strtok(). Note that this will + * not handle escaping properly. + */ + char *p, *r = *exploded_dn; +#ifdef HAVE_STRTOK_R + char *st = NULL; +#endif /* HAVE_STRTOK_R */ + +#ifndef HAVE_STRTOK_R + for (p = strtok (r, "+"); +#else /* HAVE_STRTOK_R */ + for (p = strtok_r (r, "+", &st); +#endif /* not HAVE_STRTOK_R */ + p != NULL; +#ifndef HAVE_STRTOK_R + p = strtok (NULL, "+")) +#else /* HAVE_STRTOK_R */ + p = strtok_r (NULL, "+", &st)) +#endif /* not HAVE_STRTOK_R */ + { + if (strncasecmp (p, rdnava, rdnavalen) == 0) + { + p += rdnavalen; + rdnlen = strlen (p); + if (*buflen <= rdnlen) + { + ldap_value_free (exploded_dn); + return NSS_STATUS_TRYAGAIN; + } + rdnvalue = *buffer; + strncpy (rdnvalue, p, rdnlen); + break; + } + if (r != NULL) + r = NULL; + } +#endif /* not HAVE_LDAP_EXPLODE_RDN */ + } + + if (exploded_dn != NULL) + { + ldap_value_free (exploded_dn); + } + + if (rdnvalue != NULL) + { + rdnvalue[rdnlen] = '\0'; + *buffer += rdnlen + 1; + *buflen -= rdnlen + 1; + *rval = rdnvalue; + return NSS_STATUS_SUCCESS; + } + + return NSS_STATUS_NOTFOUND; +} + +static enum ldap_map_selector +_nss_ldap_str2selector (const char *key) +{ + enum ldap_map_selector sel; + + if (!strcasecmp (key, MP_passwd)) + sel = LM_PASSWD; + else if (!strcasecmp (key, MP_shadow)) + sel = LM_SHADOW; + else if (!strcasecmp (key, MP_group)) + sel = LM_GROUP; + else if (!strcasecmp (key, MP_hosts)) + sel = LM_HOSTS; + else if (!strcasecmp (key, MP_services)) + sel = LM_SERVICES; + else if (!strcasecmp (key, MP_networks)) + sel = LM_NETWORKS; + else if (!strcasecmp (key, MP_protocols)) + sel = LM_PROTOCOLS; + else if (!strcasecmp (key, MP_rpc)) + sel = LM_RPC; + else if (!strcasecmp (key, MP_ethers)) + sel = LM_ETHERS; + else if (!strcasecmp (key, MP_netmasks)) + sel = LM_NETMASKS; + else if (!strcasecmp (key, MP_bootparams)) + sel = LM_BOOTPARAMS; + else if (!strcasecmp (key, MP_aliases)) + sel = LM_ALIASES; + else if (!strcasecmp (key, MP_netgroup)) + sel = LM_NETGROUP; + else + sel = LM_NONE; + return sel; +} + +static enum nss_status +do_parse_map_statement (struct ldap_config * cfg, + const char *statement, enum ldap_map_type type) +{ + char *key, *val; + enum ldap_map_selector sel = LM_NONE; + + key = (char *) statement; + val = key; + while (*val != ' ' && *val != '\t') + val++; + *(val++) = '\0'; + + while (*val == ' ' || *val == '\t') + val++; + + { + char *p = strchr (key, ':'); + + if (p != NULL) + { + *p = '\0'; + sel = _nss_ldap_str2selector (key); + key = ++p; + } + } + + return _nss_ldap_map_put (cfg, sel, type, key, val); +} + +/* parse a comma-separated list */ +static enum nss_status +do_parse_list (char *values, char ***valptr, + char **pbuffer, size_t *pbuflen) +{ + char *s, **p; +#ifdef HAVE_STRTOK_R + char *tok_r; +#endif /* HAVE_STRTOK_R */ + int valcount; + + int buflen = *pbuflen; + char *buffer = *pbuffer; + + /* comma separated list of values to ignore on initgroups() */ + for (valcount = 1, s = values; *s != '\0'; s++) + { + if (*s == ',') + valcount++; + } + + if (bytesleft (buffer, buflen, char *) < (valcount + 1) * sizeof (char *)) + { + return NSS_STATUS_UNAVAIL; + } + + align (buffer, buflen, char *); + p = *valptr = (char **) buffer; + + buffer += (valcount + 1) * sizeof (char *); + buflen -= (valcount + 1) * sizeof (char *); + +#ifdef HAVE_STRTOK_R + for (s = strtok_r(values, ",", &tok_r); s != NULL; + s = strtok_r(NULL, ",", &tok_r)) +#else /* HAVE_STRTOK_R */ + for (s = strtok(values, ","); s != NULL; s = strtok(NULL, ",")) +#endif /* not HAVE_STRTOK_R */ + { + int vallen; + char *elt = NULL; + + vallen = strlen (s); + if (buflen < (size_t) (vallen + 1)) + { + return NSS_STATUS_UNAVAIL; + } + + /* copy this value into the next block of buffer space */ + elt = buffer; + buffer += vallen + 1; + buflen -= vallen + 1; + + strncpy (elt, s, vallen); + elt[vallen] = '\0'; + *p++ = elt; + } + + *p = NULL; + *pbuffer = buffer; + *pbuflen = buflen; + + return NSS_STATUS_SUCCESS; +} + +static enum nss_status +do_searchdescriptorconfig (const char *key, const char *value, size_t len, + struct ldap_service_search_descriptor ** result, + char **buffer, size_t * buflen) +{ + struct ldap_service_search_descriptor **t, *cur; + char *base; + char *filter, *s; + int scope; + enum ldap_map_selector sel; + + t = NULL; + filter = NULL; + scope = -1; + + if (strncasecmp (key, NSS_LDAP_KEY_NSS_BASE_PREFIX, + NSS_LDAP_KEY_NSS_BASE_PREFIX_LEN) != 0) + return NSS_STATUS_SUCCESS; + + sel = _nss_ldap_str2selector (&key[NSS_LDAP_KEY_NSS_BASE_PREFIX_LEN]); + t = (sel < LM_NONE) ? &result[sel] : NULL; + + if (t == NULL) + return NSS_STATUS_SUCCESS; + + /* we have already checked for room for the value */ + /* len is set to the length of value */ + base = *buffer; + strncpy (base, value, len); + base[len] = '\0'; + + *buffer += len + 1; + *buflen -= len + 1; + + /* probably is some funky escaping needed here. later... */ + s = strchr (base, '?'); + if (s != NULL) + { + *s = '\0'; + s++; + if (!strcasecmp (s, "sub")) + scope = LDAP_SCOPE_SUBTREE; + else if (!strcasecmp (s, "one")) + scope = LDAP_SCOPE_ONELEVEL; + else if (!strcasecmp (s, "base")) + scope = LDAP_SCOPE_BASE; + filter = strchr (s, '?'); + if (filter != NULL) + { + *filter = '\0'; + filter++; + } + } + + if (bytesleft (*buffer, *buflen, struct ldap_service_search_descriptor) < + sizeof (struct ldap_service_search_descriptor)) + return NSS_STATUS_UNAVAIL; + + align (*buffer, *buflen, struct ldap_service_search_descriptor); + + for (cur = *t; cur && cur->lsd_next; cur = cur->lsd_next) + ; + if (!cur) + { + *t = (struct ldap_service_search_descriptor *) * buffer; + cur = *t; + } + else + { + cur->lsd_next = (struct ldap_service_search_descriptor *) * buffer; + cur = cur->lsd_next; + } + + cur->lsd_base = base; + cur->lsd_scope = scope; + cur->lsd_filter = filter; + cur->lsd_next = NULL; + + *buffer += sizeof (struct ldap_service_search_descriptor); + *buflen -= sizeof (struct ldap_service_search_descriptor); + + return NSS_STATUS_SUCCESS; +} + +static enum nss_status _nss_ldap_init_config (struct ldap_config * result) +{ + int i, j; + + memset (result, 0, sizeof (*result)); + + result->ldc_scope = LDAP_SCOPE_SUBTREE; + result->ldc_deref = LDAP_DEREF_NEVER; + result->ldc_base = NULL; + result->ldc_binddn = NULL; + result->ldc_bindpw = NULL; + result->ldc_saslid = NULL; + result->ldc_usesasl = 0; + result->ldc_rootbinddn = NULL; + result->ldc_rootbindpw = NULL; + result->ldc_rootsaslid = NULL; + result->ldc_rootusesasl = 0; +#ifdef LDAP_VERSION3 + result->ldc_version = LDAP_VERSION3; +#else /* LDAP_VERSION3 */ + result->ldc_version = LDAP_VERSION2; +#endif /* not LDAP_VERSION3 */ + result->ldc_timelimit = LDAP_NO_LIMIT; + result->ldc_bind_timelimit = 30; + result->ldc_ssl_on = SSL_OFF; + result->ldc_sslpath = NULL; + result->ldc_referrals = 1; + result->ldc_restart = 1; + result->ldc_tls_checkpeer = -1; + result->ldc_tls_cacertfile = NULL; + result->ldc_tls_cacertdir = NULL; + result->ldc_tls_ciphers = NULL; + result->ldc_tls_cert = NULL; + result->ldc_tls_key = NULL; + result->ldc_tls_randfile = NULL; + result->ldc_idle_timelimit = 0; + result->ldc_reconnect_pol = LP_RECONNECT_HARD_OPEN; + result->ldc_sasl_secprops = NULL; + result->ldc_srv_domain = NULL; + result->ldc_logdir = NULL; + result->ldc_debug = 0; + result->ldc_pagesize = LDAP_PAGESIZE; +#ifdef CONFIGURE_KRB5_CCNAME + result->ldc_krb5_ccname = NULL; +#endif /* CONFIGURE_KRB5_CCNAME */ + result->ldc_flags = 0; +#ifdef RFC2307BIS + result->ldc_flags |= NSS_LDAP_FLAGS_RFC2307BIS; +#endif /* RFC2307BIS */ +#ifdef PAGE_RESULTS + result->ldc_flags |= NSS_LDAP_FLAGS_PAGED_RESULTS; +#endif /* PAGE_RESULTS */ + result->ldc_reconnect_tries = LDAP_NSS_TRIES; + result->ldc_reconnect_sleeptime = LDAP_NSS_SLEEPTIME; + result->ldc_reconnect_maxsleeptime = LDAP_NSS_MAXSLEEPTIME; + result->ldc_reconnect_maxconntries = LDAP_NSS_MAXCONNTRIES; + result->ldc_initgroups_ignoreusers = NULL; + + for (i = 0; i <= LM_NONE; i++) + { + for (j = 0; j <= MAP_MAX; j++) + { + result->ldc_maps[i][j] = (void *)do_alloc_dictionary(); + if (result->ldc_maps[i][j] == NULL) + return NSS_STATUS_UNAVAIL; + } + } + + return NSS_STATUS_SUCCESS; +} + +enum nss_status +_nss_ldap_add_uri (struct ldap_config *result, const char *uri, + char **buffer, size_t *buflen) +{ + /* add a single URI to the list of URIs in the configuration */ + int i; + size_t uri_len; + + debug ("==> _nss_ldap_add_uri"); + + for (i = 0; result->ldc_uris[i] != NULL; i++) + ; + + if (i == NSS_LDAP_CONFIG_URI_MAX) + { + debug ("<== _nss_ldap_add_uri: maximum number of URIs exceeded"); + return NSS_STATUS_UNAVAIL; + } + + assert (i < NSS_LDAP_CONFIG_URI_MAX); + + uri_len = strlen (uri); + + if (*buflen < uri_len + 1) + return NSS_STATUS_TRYAGAIN; + + memcpy (*buffer, uri, uri_len + 1); + + result->ldc_uris[i] = *buffer; + result->ldc_uris[i + 1] = NULL; + + *buffer += uri_len + 1; + *buflen -= uri_len + 1; + + debug ("<== _nss_ldap_add_uri: added URI %s", uri); + + return NSS_STATUS_SUCCESS; +} + +static enum nss_status +do_add_uris (struct ldap_config *result, char *uris, + char **buffer, size_t *buflen) +{ + /* Add a space separated list of URIs */ + char *p; + enum nss_status status = NSS_STATUS_SUCCESS; + + for (p = uris; p != NULL; ) + { + char *q = strchr (p, ' '); + if (q != NULL) + *q = '\0'; + + status = _nss_ldap_add_uri (result, p, buffer, buflen); + + p = (q != NULL) ? ++q : NULL; + + if (status != NSS_STATUS_SUCCESS) + break; + } + + return status; +} + +static enum nss_status +do_add_hosts (struct ldap_config *result, char *hosts, + char **buffer, size_t *buflen) +{ + /* Add a space separated list of hosts */ + char *p; + enum nss_status status = NSS_STATUS_SUCCESS; + + for (p = hosts; p != NULL; ) + { + char b[NSS_LDAP_CONFIG_BUFSIZ]; + char *q = strchr (p, ' '); + + if (q != NULL) + *q = '\0'; + + snprintf (b, sizeof(b), "ldap://%s", p); + + status = _nss_ldap_add_uri (result, b, buffer, buflen); + + p = (q != NULL) ? ++q : NULL; + + if (status != NSS_STATUS_SUCCESS) + break; + } + + return status; +} + +enum nss_status +_nss_ldap_readconfig (struct ldap_config ** presult, char **buffer, size_t *buflen) +{ + FILE *fp; + char b[NSS_LDAP_CONFIG_BUFSIZ]; + enum nss_status status = NSS_STATUS_SUCCESS; + struct ldap_config *result; + struct stat statbuf; + + if (bytesleft (*buffer, *buflen, struct ldap_config *) < sizeof (struct ldap_config)) + { + return NSS_STATUS_TRYAGAIN; + } + align (*buffer, *buflen, struct ldap_config *); + result = *presult = (struct ldap_config *) *buffer; + *buffer += sizeof (struct ldap_config); + *buflen -= sizeof (struct ldap_config); + + status = _nss_ldap_init_config (result); + if (status != NSS_STATUS_SUCCESS) + { + return NSS_STATUS_SUCCESS; + } + + fp = fopen (NSS_LDAP_PATH_CONF, "r"); + if (fp == NULL) + { + return NSS_STATUS_UNAVAIL; + } + + if (fstat (fileno (fp), &statbuf) == 0) + result->ldc_mtime = statbuf.st_mtime; + else + result->ldc_mtime = 0; + + while (fgets (b, sizeof (b), fp) != NULL) + { + char *k, *v; + int len; + char **t = NULL; + + if (*b == '\n' || *b == '\r' || *b == '#') + continue; + + k = b; + v = k; + + /* skip past all characters in keyword */ + while (*v != '\0' && *v != ' ' && *v != '\t') + v++; + + if (*v == '\0') + continue; + + /* terminate keyword */ + *(v++) = '\0'; + + /* skip empty lines with more than 3 spaces at the start of the line */ + /* rds.oliver@samera.com.py 01-set-2004 */ + if (*v == '\n') + continue; + + /* skip all whitespaces between keyword and value */ + /* Lars Oergel <lars.oergel@innominate.de>, 05.10.2000 */ + while (*v == ' ' || *v == '\t') + v++; + + /* kick off all whitespaces and newline at the end of value */ + /* Bob Guo <bob@mail.ied.ac.cn>, 08.10.2001 */ + + /* Also remove \r (CR) to be able to handle files in DOS format (lines + * terminated in CR LF). Alejandro Forero Cuervo + * <azul@freaks-unidos.net>, 10-may-2005 */ + + len = strlen (v) - 1; + while (v[len] == ' ' || v[len] == '\t' || v[len] == '\n' || v[len] == '\r') + --len; + v[++len] = '\0'; + + if (*buflen < (size_t) (len + 1)) + { + status = NSS_STATUS_TRYAGAIN; + break; + } + + if (!strcasecmp (k, NSS_LDAP_KEY_HOST)) + { + status = do_add_hosts (result, v, buffer, buflen); + if (status != NSS_STATUS_SUCCESS) + break; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_URI)) + { + status = do_add_uris (result, v, buffer, buflen); + if (status != NSS_STATUS_SUCCESS) + break; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_BASE)) + { + t = &result->ldc_base; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_BINDDN)) + { + t = &result->ldc_binddn; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_BINDPW)) + { + t = &result->ldc_bindpw; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_USESASL)) + { + result->ldc_usesasl = (!strcasecmp (v, "on") + || !strcasecmp (v, "yes") + || !strcasecmp (v, "true")); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_SASLID)) + { + t = &result->ldc_saslid; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_ROOTBINDDN)) + { + t = &result->ldc_rootbinddn; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_ROOTUSESASL)) + { + result->ldc_rootusesasl = (!strcasecmp (v, "on") + || !strcasecmp (v, "yes") + || !strcasecmp (v, "true")); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_ROOTSASLID)) + { + t = &result->ldc_rootsaslid; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_SSLPATH)) + { + t = &result->ldc_sslpath; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_SCOPE)) + { + if (!strcasecmp (v, "sub")) + { + result->ldc_scope = LDAP_SCOPE_SUBTREE; + } + else if (!strcasecmp (v, "one")) + { + result->ldc_scope = LDAP_SCOPE_ONELEVEL; + } + else if (!strcasecmp (v, "base")) + { + result->ldc_scope = LDAP_SCOPE_BASE; + } + } + else if (!strcasecmp (k, NSS_LDAP_KEY_DEREF)) + { + if (!strcasecmp (v, "never")) + { + result->ldc_deref = LDAP_DEREF_NEVER; + } + else if (!strcasecmp (v, "searching")) + { + result->ldc_deref = LDAP_DEREF_SEARCHING; + } + else if (!strcasecmp (v, "finding")) + { + result->ldc_deref = LDAP_DEREF_FINDING; + } + else if (!strcasecmp (v, "always")) + { + result->ldc_deref = LDAP_DEREF_ALWAYS; + } + } + else if (!strcasecmp (k, NSS_LDAP_KEY_PORT)) + { + result->ldc_port = atoi (v); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_SSL)) + { + if (!strcasecmp (v, "on") || !strcasecmp (v, "yes") + || !strcasecmp (v, "true")) + { + result->ldc_ssl_on = SSL_LDAPS; + } + else if (!strcasecmp (v, "start_tls")) + { + result->ldc_ssl_on = SSL_START_TLS; + } + } + else if (!strcasecmp (k, NSS_LDAP_KEY_REFERRALS)) + { + result->ldc_referrals = (!strcasecmp (v, "on") + || !strcasecmp (v, "yes") + || !strcasecmp (v, "true")); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_RESTART)) + { + result->ldc_restart = (!strcasecmp (v, "on") + || !strcasecmp (v, "yes") + || !strcasecmp (v, "true")); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_LDAP_VERSION)) + { + result->ldc_version = atoi (v); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_TIMELIMIT)) + { + result->ldc_timelimit = atoi (v); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_BIND_TIMELIMIT)) + { + result->ldc_bind_timelimit = atoi (v); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_IDLE_TIMELIMIT)) + { + result->ldc_idle_timelimit = atoi (v); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_RECONNECT_POLICY)) + { + if (!strcasecmp (v, "hard") || + !strcasecmp (v, "hard_open")) + { + result->ldc_reconnect_pol = LP_RECONNECT_HARD_OPEN; + } + else if (!strcasecmp (v, "hard_init")) + { + result->ldc_reconnect_pol = LP_RECONNECT_HARD_INIT; + } + else if (!strcasecmp (v, "soft")) + { + result->ldc_reconnect_pol = LP_RECONNECT_SOFT; + } + } + else if (!strcasecmp (k, NSS_LDAP_KEY_RECONNECT_TRIES)) + { + result->ldc_reconnect_tries = atoi (v); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_RECONNECT_SLEEPTIME)) + { + result->ldc_reconnect_sleeptime = atoi (v); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_RECONNECT_MAXSLEEPTIME)) + { + result->ldc_reconnect_maxsleeptime = atoi (v); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_RECONNECT_MAXCONNTRIES)) + { + result->ldc_reconnect_maxconntries = atoi (v); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_SASL_SECPROPS)) + { + t = &result->ldc_sasl_secprops; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_LOGDIR)) + { + t = &result->ldc_logdir; + } + else if (!strcasecmp (k, NSS_LDAP_KEY_DEBUG)) + { + result->ldc_debug = atoi (v); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_PAGESIZE)) + { + result->ldc_pagesize = atoi (v); + } +#ifdef CONFIGURE_KRB5_CCNAME + else if (!strcasecmp (k, NSS_LDAP_KEY_KRB5_CCNAME)) + { + t = &result->ldc_krb5_ccname; + } +#endif /* CONFIGURE_KRB5_CCNAME */ + else if (!strcasecmp (k, "tls_checkpeer")) + { + if (!strcasecmp (v, "on") || !strcasecmp (v, "yes") + || !strcasecmp (v, "true")) + { + result->ldc_tls_checkpeer = 1; + } + else if (!strcasecmp (v, "off") || !strcasecmp (v, "no") + || !strcasecmp (v, "false")) + { + result->ldc_tls_checkpeer = 0; + } + } + else if (!strcasecmp (k, "tls_cacertfile")) + { + t = &result->ldc_tls_cacertfile; + } + else if (!strcasecmp (k, "tls_cacertdir")) + { + t = &result->ldc_tls_cacertdir; + } + else if (!strcasecmp (k, "tls_ciphers")) + { + t = &result->ldc_tls_ciphers; + } + else if (!strcasecmp (k, "tls_cert")) + { + t = &result->ldc_tls_cert; + } + else if (!strcasecmp (k, "tls_key")) + { + t = &result->ldc_tls_key; + } + else if (!strcasecmp (k, "tls_randfile")) + { + t = &result->ldc_tls_randfile; + } + else if (!strncasecmp (k, NSS_LDAP_KEY_MAP_ATTRIBUTE, + strlen (NSS_LDAP_KEY_MAP_ATTRIBUTE))) + { + do_parse_map_statement (result, v, MAP_ATTRIBUTE); + } + else if (!strncasecmp (k, NSS_LDAP_KEY_MAP_OBJECTCLASS, + strlen (NSS_LDAP_KEY_MAP_OBJECTCLASS))) + { + do_parse_map_statement (result, v, MAP_OBJECTCLASS); + } + else if (!strncasecmp (k, NSS_LDAP_KEY_SET_OVERRIDE, + strlen (NSS_LDAP_KEY_SET_OVERRIDE))) + { + do_parse_map_statement (result, v, MAP_OVERRIDE); + } + else if (!strncasecmp (k, NSS_LDAP_KEY_SET_DEFAULT, + strlen (NSS_LDAP_KEY_SET_DEFAULT))) + { + do_parse_map_statement (result, v, MAP_DEFAULT); + } + else if (!strcasecmp (k, NSS_LDAP_KEY_INITGROUPS)) + { + if (!strcasecmp (v, "backlink")) + { + result->ldc_flags |= NSS_LDAP_FLAGS_INITGROUPS_BACKLINK; + } + else + { + result->ldc_flags &= ~(NSS_LDAP_FLAGS_INITGROUPS_BACKLINK); + } + } + else if (!strcasecmp (k, NSS_LDAP_KEY_SCHEMA)) + { + if (!strcasecmp (v, "rfc2307bis")) + { + result->ldc_flags |= NSS_LDAP_FLAGS_RFC2307BIS; + } + else if (!strcasecmp (v, "rfc2307")) + { + result->ldc_flags &= ~(NSS_LDAP_FLAGS_RFC2307BIS); + } + } + else if (!strcasecmp (k, NSS_LDAP_KEY_PAGED_RESULTS)) + { + if (!strcasecmp (v, "on") + || !strcasecmp (v, "yes") + || !strcasecmp (v, "true")) + { + result->ldc_flags |= NSS_LDAP_FLAGS_PAGED_RESULTS; + } + else + { + result->ldc_flags &= ~(NSS_LDAP_FLAGS_PAGED_RESULTS); + } + } + else if (!strcasecmp (k, NSS_LDAP_KEY_INITGROUPS_IGNOREUSERS)) + { + status = do_parse_list (v, &result->ldc_initgroups_ignoreusers, + buffer, buflen); + if (status == NSS_STATUS_UNAVAIL) + { + break; + } + } + else if (!strcasecmp (k, NSS_LDAP_KEY_CONNECT_POLICY)) + { + if (!strcasecmp (v, "oneshot")) + { + result->ldc_flags |= NSS_LDAP_FLAGS_CONNECT_POLICY_ONESHOT; + } + else if (!strcasecmp (v, "persist")) + { + result->ldc_flags &= ~(NSS_LDAP_FLAGS_CONNECT_POLICY_ONESHOT); + } + } + else if (!strcasecmp (k, NSS_LDAP_KEY_SRV_DOMAIN)) + { + t = &result->ldc_srv_domain; + } + else + { + /* + * check whether the key is a naming context key + * if yes, parse; otherwise just return NSS_STATUS_SUCCESS + * so we can ignore keys we don't understand. + */ + status = + do_searchdescriptorconfig (k, v, len, result->ldc_sds, + buffer, buflen); + if (status == NSS_STATUS_UNAVAIL) + { + break; + } + } + + if (t != NULL) + { + strncpy (*buffer, v, len); + (*buffer)[len] = '\0'; + *t = *buffer; + *buffer += len + 1; + *buflen -= len + 1; + } + } + + fclose (fp); + + if (status != NSS_STATUS_SUCCESS) + { + return status; + } + + if (result->ldc_rootbinddn != NULL) + { + fp = fopen (NSS_LDAP_PATH_ROOTPASSWD, "r"); + if (fp) + { + if (fgets (b, sizeof (b), fp) != NULL) + { + int len; + + len = strlen (b); + /* BUG#138: check for newline before removing */ + if (len > 0 && b[len - 1] == '\n') + len--; + + if (*buflen < (size_t) (len + 1)) + { + return NSS_STATUS_UNAVAIL; + } + + strncpy (*buffer, b, len); + (*buffer)[len] = '\0'; + result->ldc_rootbindpw = *buffer; + *buffer += len + 1; + *buflen -= len + 1; + } + fclose (fp); + } + else if (!result->ldc_rootusesasl) + { + result->ldc_rootbinddn = NULL; + } + } + + if (result->ldc_port == 0) + { + if (result->ldc_ssl_on == SSL_LDAPS) + { + result->ldc_port = LDAPS_PORT; + } + else + { + result->ldc_port = LDAP_PORT; + } + } + + if (result->ldc_uris[0] == NULL) + { + status = NSS_STATUS_NOTFOUND; + } + + return status; +} + +enum nss_status +_nss_ldap_escape_string (const char *str, char *buf, size_t buflen) +{ + int ret = NSS_STATUS_TRYAGAIN; + char *p = buf; + char *limit = p + buflen - 3; + const char *s = str; + + while (p < limit && *s) + { + switch (*s) + { + case '*': + strcpy (p, "\\2a"); + p += 3; + break; + case '(': + strcpy (p, "\\28"); + p += 3; + break; + case ')': + strcpy (p, "\\29"); + p += 3; + break; + case '\\': + strcpy (p, "\\5c"); + p += 3; + break; + default: + *p++ = *s; + break; + } + s++; + } + + if (*s == '\0') + { + /* got to end */ + *p = '\0'; + ret = NSS_STATUS_SUCCESS; + } + + return ret; +} + +/* XXX just a linked list for now */ + +struct ldap_dictionary +{ + struct ldap_datum key; + struct ldap_datum value; + struct ldap_dictionary *next; +}; + +static struct ldap_dictionary *do_alloc_dictionary(void) +{ + struct ldap_dictionary *dict; + + dict = malloc (sizeof (*dict)); + if (dict == NULL) + { + return NULL; + } + NSS_LDAP_DATUM_ZERO (&dict->key); + NSS_LDAP_DATUM_ZERO (&dict->value); + dict->next = NULL; + + return dict; +} + +static void +do_free_datum (struct ldap_datum * datum) +{ + if (datum->data != NULL) + { + free (datum->data); + datum->data = NULL; + } + datum->size = 0; +} + +static struct ldap_dictionary * +do_find_last (struct ldap_dictionary *dict) +{ + struct ldap_dictionary *p; + + for (p = dict; p->next != NULL; p = p->next) + ; + + return p; +} + +static void +do_free_dictionary (struct ldap_dictionary *dict) +{ + do_free_datum (&dict->key); + do_free_datum (&dict->value); + free (dict); +} + +static enum nss_status +do_dup_datum (unsigned flags, struct ldap_datum * dst, const struct ldap_datum * src) +{ + dst->data = malloc (src->size); + if (dst->data == NULL) + return NSS_STATUS_TRYAGAIN; + + memcpy (dst->data, src->data, src->size); + dst->size = src->size; + + return NSS_STATUS_SUCCESS; +} + +enum nss_status +_nss_ldap_db_get (void *db, + unsigned flags, + const struct ldap_datum * key, + struct ldap_datum * value) +{ + struct ldap_dictionary *dict = (struct ldap_dictionary *) db; + struct ldap_dictionary *p; + + for (p = dict; p != NULL; p = p->next) + { + int cmp; + + if (p->key.size != key->size) + continue; + + if (flags & NSS_LDAP_DB_NORMALIZE_CASE) + cmp = strncasecmp ((char *)p->key.data, (char *)key->data, key->size); + else + cmp = memcmp (p->key.data, key->data, key->size); + + if (cmp == 0) + { + value->data = p->value.data; + value->size = p->value.size; + + return NSS_STATUS_SUCCESS; + } + } + + return NSS_STATUS_NOTFOUND; +} + +enum nss_status +_nss_ldap_db_put (void *db, + unsigned flags, + const struct ldap_datum * key, + const struct ldap_datum * value) +{ + struct ldap_dictionary *dict = (struct ldap_dictionary *) db; + struct ldap_dictionary *p, *q; + + assert (key != NULL); + assert (key->data != NULL); + + if (dict->key.data == NULL) + { + /* uninitialized */ + q = dict; + p = NULL; + } + else + { + p = do_find_last (dict); + assert (p != NULL); + assert (p->next == NULL); + q = do_alloc_dictionary(); + if (q == NULL) + return NSS_STATUS_TRYAGAIN; + } + + if (do_dup_datum (flags, &q->key, key) != NSS_STATUS_SUCCESS) + { + do_free_dictionary (q); + return NSS_STATUS_TRYAGAIN; + } + + if (do_dup_datum (flags, &q->value, value) != NSS_STATUS_SUCCESS) + { + do_free_dictionary (q); + return NSS_STATUS_TRYAGAIN; + } + + if (p != NULL) + p->next = q; + + return NSS_STATUS_SUCCESS; +} + +/* + * Add a nested netgroup or group to the namelist + */ +enum nss_status +_nss_ldap_namelist_push (struct name_list **head, const char *name) +{ + struct name_list *nl; + + debug ("==> _nss_ldap_namelist_push (%s)", name); + + nl = (struct name_list *) malloc (sizeof (*nl)); + if (nl == NULL) + { + debug ("<== _nss_ldap_namelist_push"); + return NSS_STATUS_TRYAGAIN; + } + + nl->name = strdup (name); + if (nl->name == NULL) + { + debug ("<== _nss_ldap_namelist_push"); + free (nl); + return NSS_STATUS_TRYAGAIN; + } + + nl->next = *head; + + *head = nl; + + debug ("<== _nss_ldap_namelist_push"); + + return NSS_STATUS_SUCCESS; +} + +/* + * Remove last nested netgroup or group from the namelist + */ +void +_nss_ldap_namelist_pop (struct name_list **head) +{ + struct name_list *nl; + + debug ("==> _nss_ldap_namelist_pop"); + + assert (*head != NULL); + nl = *head; + + *head = nl->next; + + assert (nl->name != NULL); + free (nl->name); + free (nl); + + debug ("<== _nss_ldap_namelist_pop"); +} + +/* + * Cleanup nested netgroup or group namelist. + */ +void +_nss_ldap_namelist_destroy (struct name_list **head) +{ + struct name_list *p, *next; + + debug ("==> _nss_ldap_namelist_destroy"); + + for (p = *head; p != NULL; p = next) + { + next = p->next; + + if (p->name != NULL) + free (p->name); + free (p); + } + + *head = NULL; + + debug ("<== _nss_ldap_namelist_destroy"); +} + +/* + * Check whether we have already seen a netgroup or group, + * to avoid loops in nested netgroup traversal + */ +int +_nss_ldap_namelist_find (struct name_list *head, const char *netgroup) +{ + struct name_list *p; + int found = 0; + + debug ("==> _nss_ldap_namelist_find"); + + for (p = head; p != NULL; p = p->next) + { + if (strcasecmp (p->name, netgroup) == 0) + { + found++; + break; + } + } + + debug ("<== _nss_ldap_namelist_find"); + + return found; +} + +enum nss_status _nss_ldap_validateconfig (struct ldap_config *config) +{ + struct stat statbuf; + + if (config == NULL) + { + return NSS_STATUS_UNAVAIL; + } + + if (config->ldc_mtime == 0) + { + return NSS_STATUS_SUCCESS; + } + + if (stat (NSS_LDAP_PATH_CONF, &statbuf) == 0) + { + return (statbuf.st_mtime > config->ldc_mtime) ? NSS_STATUS_TRYAGAIN : NSS_STATUS_SUCCESS; + } + + return NSS_STATUS_SUCCESS; +} + diff --git a/nslcd/util.h b/nslcd/util.h new file mode 100644 index 0000000..f193916 --- /dev/null +++ b/nslcd/util.h @@ -0,0 +1,121 @@ +/* + util.h - LDAP utility functions + This file was part of the nss_ldap library which has been + forked into the nss-ldapd library. + + Copyright (C) 1997-2005 Luke Howard + Copyright (C) 2006 West Consulting + Copyright (C) 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#ifndef _LDAP_NSS_LDAP_UTIL_H +#define _LDAP_NSS_LDAP_UTIL_H + +/* + * get the RDN's value: eg. if the RDN was cn=lukeh, getrdnvalue(entry) + * would return lukeh. + */ +enum nss_status _nss_ldap_getrdnvalue(LDAPMessage *entry, + const char *rdntype, + char **rval, char **buf, size_t * len); + +int _nss_ldap_write_rndvalue(FILE *fp,LDAPMessage *entry,const char *rdntype); + +/* + * map a distinguished name to a login name, or group entry + */ +enum nss_status _nss_ldap_dn2uid (const char *dn, + char **uid, char **buf, size_t * len, + int *pIsNestedGroup, LDAPMessage ** pRes); + + +#define NSS_LDAP_CONFIG_BUFSIZ 4096 + +/* + * support separate naming contexts for each map + * eventually this will support the syntax defined in + * the DUAConfigProfile searchDescriptor attribute + */ +#define NSS_LDAP_KEY_NSS_BASE_PREFIX "nss_base_" +#define NSS_LDAP_KEY_NSS_BASE_PREFIX_LEN ( sizeof(NSS_LDAP_KEY_NSS_BASE_PREFIX) - 1 ) + +/* + * Flags that are exposed via _nss_ldap_test_config_flag() + */ +#define NSS_LDAP_FLAGS_INITGROUPS_BACKLINK 0x0001 +#define NSS_LDAP_FLAGS_PAGED_RESULTS 0x0002 +#define NSS_LDAP_FLAGS_RFC2307BIS 0x0004 +#define NSS_LDAP_FLAGS_CONNECT_POLICY_ONESHOT 0x0008 + +/* + * There are a number of means of obtaining configuration information. + * + * (a) DHCP (Cf draft-hedstrom-dhc-ldap-00.txt) + * (b) a configuration file (/etc/ldap.conf) ** + * (c) a coldstart file & subsequent referrals from the LDAP server + * (d) a custom LDAP bind protocol + * (e) DNS ** + * + * This should be opaque to the rest of the library. + * ** implemented + */ + +enum nss_status _nss_ldap_readconfig (struct ldap_config ** result, char **buffer, size_t *buflen); +enum nss_status _nss_ldap_validateconfig (struct ldap_config *config); + +/* + * Escape '*' in a string for use as a filter + */ + +enum nss_status _nss_ldap_escape_string (const char *str, + char *buf, size_t buflen); + +struct ldap_datum +{ + void *data; + size_t size; +}; + +#define NSS_LDAP_DATUM_ZERO(d) do { \ + (d)->data = NULL; \ + (d)->size = 0; \ + } while (0) + +#define NSS_LDAP_DB_NORMALIZE_CASE 0x1 + +enum nss_status _nss_ldap_db_put (void *db, + unsigned flags, + const struct ldap_datum * key, + const struct ldap_datum * value); +enum nss_status _nss_ldap_db_get (void *db, + unsigned flags, + const struct ldap_datum * key, + struct ldap_datum * value); + +/* Routines for managing namelists */ + +enum nss_status _nss_ldap_namelist_push (struct name_list **head, const char *name); +void _nss_ldap_namelist_pop (struct name_list **head); +int _nss_ldap_namelist_find (struct name_list *head, const char *netgroup); +void _nss_ldap_namelist_destroy (struct name_list **head); + +enum nss_status +_nss_ldap_add_uri (struct ldap_config *result, const char *uri, + char **buffer, size_t *buflen); + +#endif /* _LDAP_NSS_LDAP_UTIL_H */ diff --git a/nslcd/xmalloc.c b/nslcd/xmalloc.c new file mode 100644 index 0000000..9dbd9a4 --- /dev/null +++ b/nslcd/xmalloc.c @@ -0,0 +1,59 @@ +/* + xmalloc.c - malloc wrapper + + Copyright (C) 2002, 2003, 2006 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> + +#include "xmalloc.h" +#include "log.h" + + +/* malloc wrapper */ +void *xmalloc(size_t size) +{ + void *tmp; + if ((tmp=malloc(size))==NULL) + { + log_log(LOG_CRIT,"malloc() failed"); + exit(1); + } + return tmp; +} + + +/* strdup wrapper */ +char *xstrdup(const char *s) +{ + char *tmp; + int l; + if (s==NULL) + { + log_log(LOG_CRIT,"xstrdup() called with NULL"); + exit(1); + } + l=strlen(s); + tmp=(char *)xmalloc((l+1)*sizeof(char)); + strncpy(tmp,s,l); + tmp[l]='\0'; + return tmp; +} diff --git a/nslcd/xmalloc.h b/nslcd/xmalloc.h new file mode 100644 index 0000000..04ad025 --- /dev/null +++ b/nslcd/xmalloc.h @@ -0,0 +1,36 @@ +/* + xmalloc.h - malloc wrapper + + Copyright (C) 2002 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#ifndef _XMALLOC_H +#define _XMALLOC_H 1 + +#include <stdlib.h> + +/* malloc wrapper */ +void *xmalloc(size_t size); + +/* allocate size for a specific type */ +#define xxmalloc(type,count) (type *)xmalloc(sizeof(type)*(count)) + +/* strdup wrapper */ +char *xstrdup(const char *s); + +#endif /* not _XMALLOC_H */ |