diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2009-12-28 12:18:12 +0000 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2009-12-28 12:18:12 +0000 |
commit | 9c0cf90c858dd020756d7cd51661beedacdf9924 (patch) | |
tree | 215b28b4e0f754819590185e02e290df814c5b74 | |
parent | 93182ac6939bece7e538bc06e5f09f47bcaddf8a (diff) |
implement attribute mapping using shell-like expressions
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss-pam-ldapd@1041 ef36b2f9-881f-0410-afb5-c4e39611909c
-rw-r--r-- | common/Makefile.am | 6 | ||||
-rw-r--r-- | common/expr.c | 207 | ||||
-rw-r--r-- | common/expr.h | 41 | ||||
-rw-r--r-- | man/nslcd.conf.5.xml | 79 | ||||
-rw-r--r-- | nslcd.conf | 2 | ||||
-rw-r--r-- | nslcd/Makefile.am | 2 | ||||
-rw-r--r-- | nslcd/attmap.c | 86 | ||||
-rw-r--r-- | nslcd/attmap.h | 18 | ||||
-rw-r--r-- | nslcd/cfg.c | 12 | ||||
-rw-r--r-- | nslcd/passwd.c | 104 | ||||
-rw-r--r-- | nslcd/shadow.c | 89 | ||||
-rw-r--r-- | tests/Makefile.am | 13 | ||||
-rw-r--r-- | tests/test_cfg.c | 1 | ||||
-rw-r--r-- | tests/test_common.c | 5 | ||||
-rw-r--r-- | tests/test_expr.c | 146 | ||||
-rw-r--r-- | tests/test_myldap.c | 5 |
16 files changed, 657 insertions, 159 deletions
diff --git a/common/Makefile.am b/common/Makefile.am index 1120b07..df90b8e 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -1,6 +1,6 @@ # Makefile.am - use automake to generate Makefile.in # -# Copyright (C) 2007, 2008 Arthur de Jong +# Copyright (C) 2007, 2008, 2009 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -17,7 +17,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -noinst_LIBRARIES = libtio.a libprot.a libdict.a +noinst_LIBRARIES = libtio.a libprot.a libdict.a libexpr.a AM_CPPFLAGS=-I$(top_srcdir) AM_CFLAGS = -fPIC @@ -28,3 +28,5 @@ libprot_a_SOURCES = nslcd-prot.c nslcd-prot.h libdict_a_SOURCES = dict.c dict.h \ set.c set.h + +libexpr_a_SOURCES = expr.c expr.h diff --git a/common/expr.c b/common/expr.c new file mode 100644 index 0000000..679d8b7 --- /dev/null +++ b/common/expr.c @@ -0,0 +1,207 @@ +/* + expr.c - limited shell-like expression parsing functions + This file is part of the nss-pam-ldapd library. + + Copyright (C) 2009 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> + +#include "expr.h" + +/* the maximum length of a variable name */ +#define MAXVARLENGTH 30 + +static inline int my_isalpha(const char c) +{ + return ((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z')); +} + +static inline int my_isalphanum(const char c) +{ + return my_isalpha(c)||((c>='0')&&(c<='9')); +} + +#include <stdio.h> + +/* return the part of the string that is a valid name */ +MUST_USE static const char *parse_name(const char *str,int *ptr,char *buffer,size_t buflen) +{ + int i=0; + /* clear the buffer */ + buffer[i]='\0'; + /* look for an alpha+alphanumeric* string */ + if (!my_isalpha(str[*ptr])) + return NULL; + while (my_isalphanum(str[*ptr])) + { + if ((size_t)i>=buflen) + return NULL; + buffer[i++]=str[(*ptr)++]; + } + /* NULL-terminate the string */ + if ((size_t)i>=buflen) + return NULL; + buffer[i++]='\0'; + return buffer; +} + +/* definition of the parse functions (they call eachother) */ +MUST_USE static const char *parse_dollar_expression( + const char *str,int *ptr,char *buffer,size_t buflen, + expander_t expander,void *expander_arg); +MUST_USE static const char *parse_expression( + const char *str,int *ptr,int endat,char *buffer,size_t buflen, + expander_t expander,void *expander_arg); + +MUST_USE static const char *parse_dollar_expression( + const char *str,int *ptr,char *buffer,size_t buflen, + expander_t expander,void *expander_arg) +{ + char varname[MAXVARLENGTH]; + const char *varvalue; + if ((buflen<=0)||(buffer==NULL)||(str==NULL)||(ptr==NULL)) + return NULL; + if (str[*ptr]=='{') + { + (*ptr)++; + /* the first part is always a variable name */ + if (parse_name(str,ptr,varname,sizeof(varname))==NULL) + return NULL; + varvalue=expander(varname,expander_arg); + if (str[*ptr]=='}') + { + /* simple substitute */ + if (strlen(varvalue)>=buflen) + return NULL; + strcpy(buffer,varvalue); + } + else if (strncmp(str+*ptr,":-",2)==0) + { + /* if variable is not set or empty, substitute remainder */ + (*ptr)+=2; + if (parse_expression(str,ptr,'}',buffer,buflen,expander,expander_arg)==NULL) + return NULL; + if ((varvalue!=NULL)&&(*varvalue!='\0')) + { + if (strlen(varvalue)>=buflen) + return NULL; + strcpy(buffer,varvalue); + } + } + else if (strncmp(str+*ptr,":+",2)==0) + { + /* if variable is set, substitute remainer */ + (*ptr)+=2; + if (parse_expression(str,ptr,'}',buffer,buflen,expander,expander_arg)==NULL) + return NULL; + if ((varvalue==NULL)||(*varvalue=='\0')) + buffer[0]='\0'; + } + else + return NULL; + (*ptr)++; /* skip closing } */ + } + else + { + /* it is a simple reference to a variable, like $uidNumber */ + if (parse_name(str,ptr,varname,sizeof(varname))==NULL) + return NULL; + varvalue=expander(varname,expander_arg); + if (strlen(varvalue)>=buflen) + return NULL; + strcpy(buffer,varvalue); + } + return buffer; +} + +MUST_USE static const char *parse_expression( + const char *str,int *ptr,int endat,char *buffer,size_t buflen, + expander_t expander,void *expander_arg) +{ + int j=0; + /* go over string */ + while ((str[*ptr]!=endat)&&(str[*ptr]!='\0')) + { + switch (str[*ptr]) + { + case '$': /* beginning of an expression */ + (*ptr)++; + if ((size_t)j>=buflen) + return NULL; + if (parse_dollar_expression(str,ptr,buffer+j,buflen-j,expander,expander_arg)==NULL) + return NULL; + j=strlen(buffer); + break; + case '\\': /* escaped character, unescape */ + (*ptr)++; + default: /* just copy the text */ + if ((size_t)j>=buflen) + return NULL; + buffer[j++]=str[*ptr]; + (*ptr)++; + } + } + /* NULL-terminate buffer */ + if ((size_t)j>=buflen) + return NULL; + buffer[j++]='\0'; + return buffer; +} + +MUST_USE const char *expr_parse(const char *str,char *buffer,size_t buflen, + expander_t expander,void *expander_arg) + +{ + int i=0; + return parse_expression(str,&i,'\0',buffer,buflen,expander,expander_arg); +} + +SET *expr_vars(const char *str,SET *set) +{ + char varname[MAXVARLENGTH]; + int i=0; + /* allocate set if needed */ + if (set==NULL) + set=set_new(); + if (set==NULL) + return NULL; + /* go over string */ + while (str[i]!='\0') + { + switch (str[i]) + { + case '$': /* beginning of a $-expression */ + i++; + if (str[i]=='{') + i++; + /* the rest should start with a variable name */ + if (parse_name(str,&i,varname,sizeof(varname))!=NULL) + set_add(set,varname); + break; + case '\\': /* escaped character, unescape */ + i++; + default: /* just skip */ + i++; + } + } + return set; +} diff --git a/common/expr.h b/common/expr.h new file mode 100644 index 0000000..9861a49 --- /dev/null +++ b/common/expr.h @@ -0,0 +1,41 @@ +/* + expr.h - limited shell-like expression parsing functions + This file is part of the nss-pam-ldapd library. + + Copyright (C) 2009 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _EXPR_H +#define _EXPR_H 1 + +#include "compat/attrs.h" +#include "common/set.h" + +typedef const char *(*expander_t)(const char *name,void *expander_arg); + +/* Parse the expression and store the result in buffer, using the + expander function to expand variable names to values. If the expression + is invalid or the result didn't fit in the buffer NULL is returned. */ +MUST_USE const char *expr_parse(const char *expr,char *buffer,size_t buflen, + expander_t expander,void *expander_arg); + +/* Return the variable names that are used in expr. If set is NULL a new one + is allocated, otherwise the passed set is added to. */ +SET *expr_vars(const char *expr,SET *set); + +#endif /* not _EXPR_H */ diff --git a/man/nslcd.conf.5.xml b/man/nslcd.conf.5.xml index b553354..704bd58 100644 --- a/man/nslcd.conf.5.xml +++ b/man/nslcd.conf.5.xml @@ -375,13 +375,13 @@ <code>ipProtocolNumber</code> or <code>macAddress</code>). The <emphasis remap="I">NEWATTRIBUTE</emphasis> may be any attribute as it is available in the directory. -<!-- + </para> + <para> If the <emphasis remap="I">NEWATTRIBUTE</emphasis> is presented in - quotes (") the specfied value will be used instead of looking up the - value in the directory. - Specifies a value to use for the specified attribute in preference - to that contained in the actual entry. ---> + quotes (") it is treated as an expression which will be evaluated + to build up the actual value used. + Not all attributes can be mapped this way. + See the section on attribute mapping below for more details. </para> </listitem> </varlistentry> @@ -686,6 +686,73 @@ </variablelist> </refsect1> + <refsect1 id="attmappingexpressions"> + <title>Attribute mapping expressions</title> + <para> + For some attributes a mapping expression may be used to construct the + resulting value. This is currently only possible for attributes that do + not need to be used in search filters. + </para> + <para> + The expressions are a subset of the double quoted string expressions in the + Bourne (POSIX) shell. + Instead of variable substitution, attribute lookups are done on the current + entry and the attribute value is substituted. + The following expressions are supported: + </para> + <variablelist remap="TP"> + <varlistentry> + <term><literal>${attr}</literal> (or <literal>$attr</literal> for short)</term> + <listitem><para> + will substitute the value of the attribute + </para></listitem> + </varlistentry> + <varlistentry> + <term><literal>${attr:-word}</literal></term> + <listitem><para> + (use default) will substitbute the value of the attribute or, if the + attribute is not set or empty substitute the word + </para></listitem> + </varlistentry> + <varlistentry> + <term><literal>${attr:+word}</literal></term> + <listitem><para> + (use alternative) will substitbute word if attribute is set, otherwise + substitute the empty string + </para></listitem> + </varlistentry> + </variablelist> + <para> + The <command>nslcd</command> daemon checks the expressions to figure + out which attributes to fetch from <acronym>LDAP</acronym>. + Some examples to demonstrate how these expressions may be used in + attribute mapping: + </para> + <variablelist remap="TP"> + <varlistentry> + <term><literal>"${shadowFlag:-0}"</literal></term> + <listitem><para> + use the <literal>shadowFlag</literal> attribute, using the + value 0 as default + </para></listitem> + </varlistentry> + <varlistentry> + <term><literal>"/home/$uid/$uid"</literal></term> + <listitem><para> + use the <literal>uid</literal> attribute to build a + <literal>homeDirectory</literal> value if that attribute is missing + </para></listitem> + </varlistentry> + <varlistentry> + <term><literal>"${isDisabled:+100}"</literal></term> + <listitem><para> + if the <literal>isDisabled</literal> attribute is set, return 100, + otherwise leave value empty + </para></listitem> + </varlistentry> + </variablelist> + </refsect1> + <refsect1 id="files"> <title>Files</title> <variablelist remap="TP"> @@ -91,7 +91,7 @@ base dc=example,dc=com #map passwd uid msSFUName #map passwd userPassword msSFUPassword #map passwd homeDirectory msSFUHomeDirectory -#map passwd cn msSFUName +#map passwd gecos msSFUName #filter shadow (objectClass=User) #map shadow uid msSFUName #map shadow userPassword msSFUPassword diff --git a/nslcd/Makefile.am b/nslcd/Makefile.am index bdf3150..3989c4a 100644 --- a/nslcd/Makefile.am +++ b/nslcd/Makefile.am @@ -33,4 +33,4 @@ nslcd_SOURCES = nslcd.c ../nslcd.h ../common/nslcd-prot.h \ alias.c ether.c group.c host.c netgroup.c network.c \ passwd.c protocol.c rpc.c service.c shadow.c pam.c nslcd_LDADD = @nslcd_LIBS@ ../common/libtio.a ../common/libdict.a \ - ../compat/libcompat.a + ../common/libexpr.a ../compat/libcompat.a diff --git a/nslcd/attmap.c b/nslcd/attmap.c index 765d178..f703345 100644 --- a/nslcd/attmap.c +++ b/nslcd/attmap.c @@ -26,8 +26,8 @@ #include <strings.h> #include "attmap.h" - -const char *attmap_objectClass = "objectClass"; +#include "log.h" +#include "common/expr.h" /* these are the bases that are defined per database */ extern const char *alias_bases[]; @@ -172,7 +172,6 @@ const char **attmap_get_var(enum ldap_map_selector map,const char *name) if (strcasecmp(name,"uidNumber")==0) return &attmap_passwd_uidNumber; if (strcasecmp(name,"gidNumber")==0) return &attmap_passwd_gidNumber; if (strcasecmp(name,"gecos")==0) return &attmap_passwd_gecos; - if (strcasecmp(name,"cn")==0) return &attmap_passwd_cn; if (strcasecmp(name,"homeDirectory")==0) return &attmap_passwd_homeDirectory; if (strcasecmp(name,"loginShell")==0) return &attmap_passwd_loginShell; } @@ -206,3 +205,84 @@ const char **attmap_get_var(enum ldap_map_selector map,const char *name) } return NULL; } + +const char *attmap_set_mapping(const char **var,const char *value) +{ + /* check if we are setting an expression */ + if (value[0]=='"') + { + /* these attributes may contain an expression + (note that this needs to match the functionality in the specific + lookup module) */ + if ( (var!=&attmap_passwd_gidNumber) && + (var!=&attmap_passwd_gecos) && + (var!=&attmap_passwd_homeDirectory) && + (var!=&attmap_passwd_loginShell) && + (var!=&attmap_shadow_shadowLastChange) && + (var!=&attmap_shadow_shadowMin) && + (var!=&attmap_shadow_shadowMax) && + (var!=&attmap_shadow_shadowWarning) && + (var!=&attmap_shadow_shadowInactive) && + (var!=&attmap_shadow_shadowExpire) && + (var!=&attmap_shadow_shadowFlag) ) + return NULL; + } + /* check if the value will be changed */ + if ( (*var==NULL) || (strcmp(*var,value)!=0) ) + *var=strdup(value); + return *var; +} + +static const char *entry_expand(const char *name,void *expander_attr) +{ + MYLDAP_ENTRY *entry=(MYLDAP_ENTRY *)expander_attr; + const char **values; + if (strcasecmp(name,"dn")==0) + return myldap_get_dn(entry); + values=myldap_get_values(entry,name); + if (values==NULL) + return ""; + /* TODO: handle userPassword attribute specially */ + if ((values[0]!=NULL)&&(values[1]!=NULL)) + { + log_log(LOG_WARNING,"entry %s contains multiple %s values", + myldap_get_dn(entry),name); + } + return values[0]; +} + +MUST_USE const char *attmap_get_value(MYLDAP_ENTRY *entry,const char *attr,char *buffer,size_t buflen) +{ + const char **values; + /* for simple values just return the attribute */ + if (attr[0]!='"') + { + values=myldap_get_values(entry,attr); + if (values==NULL) + return NULL; + strncpy(buffer,values[0],buflen); + buffer[buflen-1]='\0'; + return buffer; + /* TODO: maybe warn when multiple values are found */ + } + if ( (attr[strlen(attr)-1]!='"') || + (expr_parse(attr+1,buffer,buflen,entry_expand,(void *)entry)==NULL) ) + { + log_log(LOG_ERR,"attribute mapping %s is invalid",attr); + buffer[0]='\0'; + return NULL; + } + /* strip trailing " */ + if (buffer[strlen(buffer)-1]=='"') + buffer[strlen(buffer)-1]='\0'; + return buffer; +} + +SET *attmap_add_attributes(SET *set,const char *attr) +{ + if (attr[0]!='\"') + set_add(set,attr); + else + expr_vars(attr,set); + return set; +} diff --git a/nslcd/attmap.h b/nslcd/attmap.h index 7ec9177..48578c1 100644 --- a/nslcd/attmap.h +++ b/nslcd/attmap.h @@ -2,7 +2,7 @@ attmap.h - attribute mapping variables This file is part of the nss-pam-ldapd library. - Copyright (C) 2007, 2008 Arthur de Jong + Copyright (C) 2007, 2008, 2009 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -24,6 +24,8 @@ #define _ATTMAP_H 1 #include "cfg.h" +#include "myldap.h" +#include "common/set.h" /* these are the attribute names per database */ extern const char *attmap_alias_cn; @@ -49,7 +51,6 @@ extern const char *attmap_passwd_userPassword; extern const char *attmap_passwd_uidNumber; extern const char *attmap_passwd_gidNumber; extern const char *attmap_passwd_gecos; -extern const char *attmap_passwd_cn; extern const char *attmap_passwd_homeDirectory; extern const char *attmap_passwd_loginShell; extern const char *attmap_protocol_cn; @@ -83,4 +84,17 @@ const char **filter_get_var(enum ldap_map_selector map); underscode replaced by a dot (e.g passwd.homeDirectory) */ const char **attmap_get_var(enum ldap_map_selector map,const char *name); +/* Set the attribute mapping of the variable to the value specified. + Returns the new value on success. */ + +const char *attmap_set_mapping(const char **var,const char *value); + +/* Return a value for the attribute, handling the case where attr + is an expression. */ +const char *attmap_get_value(MYLDAP_ENTRY *entry,const char *attr,char *buffer,size_t buflen); + +/* Add the attributes from attr to the set. The attr argumenent + can either be an attribute or an attribute expression. */ +SET *attmap_add_attributes(SET *set,const char *attr); + #endif /* not _ATTMAP_H */ diff --git a/nslcd/cfg.c b/nslcd/cfg.c index e8a5408..060b566 100644 --- a/nslcd/cfg.c +++ b/nslcd/cfg.c @@ -623,7 +623,7 @@ static void parse_map_statement(const char *filename,int lnr, { enum ldap_map_selector map; const char **var; - char oldatt[32], newatt[32]; + char oldatt[32], newatt[1024]; /* get the map */ if ((map=get_map(&line))==LM_NONE) { @@ -636,19 +636,17 @@ static void parse_map_statement(const char *filename,int lnr, (get_token(&line,newatt,sizeof(newatt))!=NULL)); /* check that there are no more tokens left on the line */ get_eol(filename,lnr,keyword,&line); - /* get the attribute variable to set */ + /* change attribute mapping */ var=attmap_get_var(map,oldatt); if (var==NULL) { log_log(LOG_ERR,"%s:%d: unknown attribute to map: '%s'",filename,lnr,oldatt); exit(EXIT_FAILURE); } - /* check if the value will be changed */ - if ( (*var==NULL) || (strcmp(*var,newatt)!=0) ) + if (attmap_set_mapping(var,newatt)==NULL) { - /* Note: we have a memory leak here if a single mapping is changed - multiple times in one config (deemed not a problem) */ - *var=xstrdup(newatt); + log_log(LOG_ERR,"%s:%d: attribute %s cannot be an expression",filename,lnr,oldatt); + exit(EXIT_FAILURE); } } diff --git a/nslcd/passwd.c b/nslcd/passwd.c index 63bfce8..a5149f5 100644 --- a/nslcd/passwd.c +++ b/nslcd/passwd.c @@ -59,17 +59,14 @@ const char *attmap_passwd_uid = "uid"; const char *attmap_passwd_userPassword = "userPassword"; const char *attmap_passwd_uidNumber = "uidNumber"; const char *attmap_passwd_gidNumber = "gidNumber"; -const char *attmap_passwd_gecos = "gecos"; -const char *attmap_passwd_cn = "cn"; +const char *attmap_passwd_gecos = "\"${gecos:-$cn}\""; const char *attmap_passwd_homeDirectory = "homeDirectory"; const char *attmap_passwd_loginShell = "loginShell"; /* default values for attributes */ static const char *default_passwd_userPassword = "*"; /* unmatchable */ -static const char *default_passwd_homeDirectory = ""; -static const char *default_passwd_loginShell = ""; -/* Note that the password value should be one of: +/* Note that the resulting password value should be one of: <empty> - no password set, allow login without password * - often used to prevent logins x - "valid" encrypted password that does not match any valid password @@ -77,7 +74,7 @@ static const char *default_passwd_loginShell = ""; other - encrypted password, usually in crypt(3) format */ /* the attribute list to request with searches */ -static const char *passwd_attrs[10]; +static const char **passwd_attrs=NULL; /* create a search filter for searching a passwd entry by name, return -1 on errors */ @@ -109,6 +106,7 @@ static int mkfilter_passwd_byuid(uid_t uid, void passwd_init(void) { int i; + SET *set; /* set up search bases */ if (passwd_bases[0]==NULL) for (i=0;i<NSS_LDAP_CONFIG_MAX_BASES;i++) @@ -117,16 +115,17 @@ void passwd_init(void) if (passwd_scope==LDAP_SCOPE_DEFAULT) passwd_scope=nslcd_cfg->ldc_scope; /* set up attribute list */ - passwd_attrs[0]=attmap_passwd_uid; - passwd_attrs[1]=attmap_passwd_userPassword; - passwd_attrs[2]=attmap_passwd_uidNumber; - passwd_attrs[3]=attmap_passwd_gidNumber; - passwd_attrs[4]=attmap_passwd_cn; - passwd_attrs[5]=attmap_passwd_homeDirectory; - passwd_attrs[6]=attmap_passwd_loginShell; - passwd_attrs[7]=attmap_passwd_gecos; - passwd_attrs[8]="objectClass"; - passwd_attrs[9]=NULL; + set=set_new(); + attmap_add_attributes(set,"objectClass"); /* for testing shadowAccount */ + attmap_add_attributes(set,attmap_passwd_uid); + attmap_add_attributes(set,attmap_passwd_userPassword); + attmap_add_attributes(set,attmap_passwd_uidNumber); + attmap_add_attributes(set,attmap_passwd_gidNumber); + attmap_add_attributes(set,attmap_passwd_gecos); + attmap_add_attributes(set,attmap_passwd_homeDirectory); + attmap_add_attributes(set,attmap_passwd_loginShell); + passwd_attrs=set_tolist(set); + set_free(set); } /* the cache that is used in dn2uid() */ @@ -301,10 +300,11 @@ static int write_passwd(TFILE *fp,MYLDAP_ENTRY *entry,const char *requser, const char *passwd; uid_t uids[MAXUIDS_PER_ENTRY]; int numuids; + char gidbuf[10]; gid_t gid; - const char *gecos; - const char *homedir; - const char *shell; + char gecos[100]; + char homedir[100]; + char shell[100]; int i,j; /* get the usernames for this entry */ usernames=myldap_get_values(entry,attmap_passwd_uid); @@ -353,77 +353,29 @@ static int write_passwd(TFILE *fp,MYLDAP_ENTRY *entry,const char *requser, } } /* get the gid for this entry */ - tmpvalues=myldap_get_values(entry,attmap_passwd_gidNumber); - if ((tmpvalues==NULL)||(tmpvalues[0]==NULL)) + attmap_get_value(entry,attmap_passwd_gidNumber,gidbuf,sizeof(gidbuf)); + if (gidbuf[0]=='\0') { log_log(LOG_WARNING,"passwd entry %s does not contain %s value", myldap_get_dn(entry),attmap_passwd_gidNumber); return 0; } - else if (tmpvalues[1]!=NULL) - { - log_log(LOG_WARNING,"passwd entry %s contains multiple %s values", - myldap_get_dn(entry),attmap_passwd_gidNumber); - } - gid=(gid_t)strtol(tmpvalues[0],&tmp,0); - if ((*(tmpvalues[0])=='\0')||(*tmp!='\0')) + gid=(gid_t)strtol(gidbuf,&tmp,0); + if ((gidbuf[0]=='\0')||(*tmp!='\0')) { log_log(LOG_WARNING,"passwd entry %s contains non-numeric %s value", myldap_get_dn(entry),attmap_passwd_gidNumber); return 0; } - /* get the gecos for this entry (fall back to cn) */ - tmpvalues=myldap_get_values(entry,attmap_passwd_gecos); - if ((tmpvalues==NULL)||(tmpvalues[0]==NULL)) - tmpvalues=myldap_get_values(entry,attmap_passwd_cn); - if ((tmpvalues==NULL)||(tmpvalues[0]==NULL)) - { - log_log(LOG_WARNING,"passwd entry %s does not contain %s or %s value", - myldap_get_dn(entry),attmap_passwd_gecos,attmap_passwd_cn); - return 0; - } - else if (tmpvalues[1]!=NULL) - { - log_log(LOG_WARNING,"passwd entry %s contains multiple %s or %s values", - myldap_get_dn(entry),attmap_passwd_gecos,attmap_passwd_cn); - } - gecos=tmpvalues[0]; + /* get the gecos for this entry */ + attmap_get_value(entry,attmap_passwd_gecos,gecos,sizeof(gecos)); /* get the home directory for this entry */ - tmpvalues=myldap_get_values(entry,attmap_passwd_homeDirectory); - if ((tmpvalues==NULL)||(tmpvalues[0]==NULL)) - { + attmap_get_value(entry,attmap_passwd_homeDirectory,homedir,sizeof(homedir)); + if (homedir[0]=='\0') log_log(LOG_WARNING,"passwd entry %s does not contain %s value", myldap_get_dn(entry),attmap_passwd_homeDirectory); - homedir=default_passwd_homeDirectory; - } - else - { - if (tmpvalues[1]!=NULL) - { - log_log(LOG_WARNING,"passwd entry %s contains multiple %s values", - myldap_get_dn(entry),attmap_passwd_homeDirectory); - } - homedir=tmpvalues[0]; - if (*homedir=='\0') - homedir=default_passwd_homeDirectory; - } /* get the shell for this entry */ - tmpvalues=myldap_get_values(entry,attmap_passwd_loginShell); - if ((tmpvalues==NULL)||(tmpvalues[0]==NULL)) - { - shell=default_passwd_loginShell; - } - else - { - if (tmpvalues[1]!=NULL) - { - log_log(LOG_WARNING,"passwd entry %s contains multiple %s values", - myldap_get_dn(entry),attmap_passwd_loginShell); - } - shell=tmpvalues[0]; - if (*shell=='\0') - shell=default_passwd_loginShell; - } + attmap_get_value(entry,attmap_passwd_loginShell,shell,sizeof(shell)); /* write the entries */ for (i=0;usernames[i]!=NULL;i++) if ((requser==NULL)||(strcmp(requser,usernames[i])==0)) diff --git a/nslcd/shadow.c b/nslcd/shadow.c index ac00bda..4cb7067 100644 --- a/nslcd/shadow.c +++ b/nslcd/shadow.c @@ -55,26 +55,19 @@ const char *shadow_filter = "(objectClass=shadowAccount)"; /* the attributes to request with searches */ const char *attmap_shadow_uid = "uid"; const char *attmap_shadow_userPassword = "userPassword"; -const char *attmap_shadow_shadowLastChange = "shadowLastChange"; -const char *attmap_shadow_shadowMin = "shadowMin"; -const char *attmap_shadow_shadowMax = "shadowMax"; -const char *attmap_shadow_shadowWarning = "shadowWarning"; -const char *attmap_shadow_shadowInactive = "shadowInactive"; -const char *attmap_shadow_shadowExpire = "shadowExpire"; -const char *attmap_shadow_shadowFlag = "shadowFlag"; +const char *attmap_shadow_shadowLastChange = "\"${shadowLastChange:--1}\""; +const char *attmap_shadow_shadowMin = "\"${shadowMin:--1}\""; +const char *attmap_shadow_shadowMax = "\"${shadowMax:--1}\""; +const char *attmap_shadow_shadowWarning = "\"${shadowWarning:--1}\""; +const char *attmap_shadow_shadowInactive = "\"${shadowInactive:--1}\""; +const char *attmap_shadow_shadowExpire = "\"${shadowExpire:--1}\""; +const char *attmap_shadow_shadowFlag = "\"${shadowFlag:-0}\""; /* default values for attributes */ static const char *default_shadow_userPassword = "*"; /* unmatchable */ -static const char *default_shadow_shadowLastChange = "-1"; -static const char *default_shadow_shadowMin = "-1"; -static const char *default_shadow_shadowMax = "-1"; -static const char *default_shadow_shadowWarning = "-1"; -static const char *default_shadow_shadowInactive = "-1"; -static const char *default_shadow_shadowExpire = "-1"; -static const char *default_shadow_shadowFlag = "0"; /* the attribute list to request with searches */ -static const char *shadow_attrs[10]; +static const char **shadow_attrs=NULL; static int mkfilter_shadow_byname(const char *name, char *buffer,size_t buflen) @@ -93,6 +86,7 @@ static int mkfilter_shadow_byname(const char *name, void shadow_init(void) { int i; + SET *set; /* set up search bases */ if (shadow_bases[0]==NULL) for (i=0;i<NSS_LDAP_CONFIG_MAX_BASES;i++) @@ -101,16 +95,18 @@ void shadow_init(void) if (shadow_scope==LDAP_SCOPE_DEFAULT) shadow_scope=nslcd_cfg->ldc_scope; /* set up attribute list */ - shadow_attrs[0]=attmap_shadow_uid; - shadow_attrs[1]=attmap_shadow_userPassword; - shadow_attrs[2]=attmap_shadow_shadowLastChange; - shadow_attrs[3]=attmap_shadow_shadowMax; - shadow_attrs[4]=attmap_shadow_shadowMin; - shadow_attrs[5]=attmap_shadow_shadowWarning; - shadow_attrs[6]=attmap_shadow_shadowInactive; - shadow_attrs[7]=attmap_shadow_shadowExpire; - shadow_attrs[8]=attmap_shadow_shadowFlag; - shadow_attrs[9]=NULL; + set=set_new(); + attmap_add_attributes(set,attmap_shadow_uid); + attmap_add_attributes(set,attmap_shadow_userPassword); + attmap_add_attributes(set,attmap_shadow_shadowLastChange); + attmap_add_attributes(set,attmap_shadow_shadowMax); + attmap_add_attributes(set,attmap_shadow_shadowMin); + attmap_add_attributes(set,attmap_shadow_shadowWarning); + attmap_add_attributes(set,attmap_shadow_shadowInactive); + attmap_add_attributes(set,attmap_shadow_shadowExpire); + attmap_add_attributes(set,attmap_shadow_shadowFlag); + shadow_attrs=set_tolist(set); + set_free(set); } static long to_date(const char *date,const char *attr) @@ -156,43 +152,27 @@ static long to_date(const char *date,const char *attr) #endif #define GET_OPTIONAL_LONG(var,att) \ - tmpvalues=myldap_get_values(entry,attmap_shadow_##att); \ - if ((tmpvalues==NULL)||(tmpvalues[0]==NULL)) \ - var=strtol(default_shadow_##att,NULL,0); \ - else \ + tmpvalue=attmap_get_value(entry,attmap_shadow_##att,buffer,sizeof(buffer)); \ + if (tmpvalue==NULL) \ + tmpvalue=""; \ + var=strtol(tmpvalue,&tmp,0); \ + if ((*(tmpvalue)=='\0')||(*tmp!='\0')) \ { \ - if (tmpvalues[1]!=NULL) \ - { \ - log_log(LOG_WARNING,"shadow entry %s contains multiple %s values", \ - myldap_get_dn(entry),attmap_shadow_##att); \ - } \ - var=strtol(tmpvalues[0],&tmp,0); \ - if ((*(tmpvalues[0])=='\0')||(*tmp!='\0')) \ - { \ - log_log(LOG_WARNING,"shadow entry %s contains non-numeric %s value", \ - myldap_get_dn(entry),attmap_shadow_##att); \ - return 0; \ - } \ + log_log(LOG_WARNING,"shadow entry %s contains non-numeric %s value", \ + myldap_get_dn(entry),attmap_shadow_##att); \ + return 0; \ } #define GET_OPTIONAL_DATE(var,att) \ - tmpvalues=myldap_get_values(entry,attmap_shadow_##att); \ - if ((tmpvalues==NULL)||(tmpvalues[0]==NULL)) \ - var=to_date(default_shadow_##att,attmap_shadow_##att); \ - else \ - { \ - if (tmpvalues[1]!=NULL) \ - { \ - log_log(LOG_WARNING,"shadow entry %s contains multiple %s values", \ - myldap_get_dn(entry),attmap_shadow_##att); \ - } \ - var=to_date(tmpvalues[0],attmap_shadow_##att); \ - } + tmpvalue=attmap_get_value(entry,attmap_shadow_##att,buffer,sizeof(buffer)); \ + if (tmpvalue==NULL) \ + tmpvalue=""; \ + var=to_date(tmpvalue,attmap_shadow_##att); static int write_shadow(TFILE *fp,MYLDAP_ENTRY *entry,const char *requser) { int32_t tmpint32; - const char **tmpvalues; + const char *tmpvalue; char *tmp; const char **usernames; const char *passwd; @@ -204,6 +184,7 @@ static int write_shadow(TFILE *fp,MYLDAP_ENTRY *entry,const char *requser) long expiredate; unsigned long flag; int i; + char buffer[80]; /* get username */ usernames=myldap_get_values(entry,attmap_shadow_uid); if ((usernames==NULL)||(usernames[0]==NULL)) diff --git a/tests/Makefile.am b/tests/Makefile.am index feac01d..a0be82c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,7 +1,7 @@ # Makefile.am - use automake to generate Makefile.in # # Copyright (C) 2006 West Consulting -# Copyright (C) 2006, 2007, 2008 Arthur de Jong +# Copyright (C) 2006, 2007, 2008, 2009 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -19,10 +19,10 @@ # 02110-1301 USA TESTS = test_dict test_set test_tio test_cfg test_myldap.sh test_nsscmds.sh \ - test_getpeercred test_common + test_getpeercred test_common test_expr check_PROGRAMS = test_dict test_set test_tio test_cfg test_myldap \ - test_getpeercred test_common + test_getpeercred test_common test_expr EXTRA_PROGRAMS = test_aliases test_ethers test_group test_hosts \ test_netgroup test_networks test_passwd test_protocols \ @@ -52,14 +52,17 @@ test_tio_SOURCES = test_tio.c ../common/tio.h ../common/tio.c test_tio_LDFLAGS = $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) test_cfg_SOURCES = test_cfg.c -test_cfg_LDADD = ../nslcd/log.o ../nslcd/attmap.o \ +test_cfg_LDADD = ../nslcd/log.o ../nslcd/attmap.o \ ../nslcd/common.o ../nslcd/myldap.o \ ../nslcd/alias.o ../nslcd/ether.o ../nslcd/group.o \ ../nslcd/host.o ../nslcd/netgroup.o ../nslcd/network.o \ ../nslcd/passwd.o ../nslcd/protocol.o ../nslcd/rpc.o \ ../nslcd/service.o ../nslcd/shadow.o \ @nslcd_LIBS@ ../common/libtio.a ../common/libdict.a \ - ../compat/libcompat.a + ../common/libexpr.a ../compat/libcompat.a + +test_expr_SOURCES = test_expr.c +test_expr_LDADD = ../common/set.o ../common/dict.o test_myldap_SOURCES = test_myldap.c test_myldap_LDADD = ../nslcd/log.o ../nslcd/common.o ../nslcd/cfg.o \ diff --git a/tests/test_cfg.c b/tests/test_cfg.c index 269b57c..dc558c2 100644 --- a/tests/test_cfg.c +++ b/tests/test_cfg.c @@ -206,6 +206,7 @@ static void test_read(void) "base dc=test, dc=tld\n" "base passwd ou=Some People,dc=test,dc=tld\n" "map\tpasswd uid\t\tsAMAccountName\n" + "map passwd homeDirectory \"${homeDirectory:-/home/$uid}\"\n" "filter group (&(objeclClass=posixGroup)(gid=1*))\n" "\n" "scope passwd one\n"); diff --git a/tests/test_common.c b/tests/test_common.c index 89856d4..4831815 100644 --- a/tests/test_common.c +++ b/tests/test_common.c @@ -2,7 +2,7 @@ test_common.c - simple test for the common module This file is part of the nss-pam-ldapd library. - Copyright (C) 2008 Arthur de Jong + Copyright (C) 2008, 2009 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -32,6 +32,9 @@ const char **base_get_var(int UNUSED(map)) {return NULL;} int *scope_get_var(int UNUSED(map)) {return NULL;} const char **filter_get_var(int UNUSED(map)) {return NULL;} const char **attmap_get_var(int UNUSED(map),const char UNUSED(*name)) {return NULL;} +const char *attmap_get_value(MYLDAP_ENTRY UNUSED(*entry),const char UNUSED(*attr),char UNUSED(*buffer),size_t UNUSED(buflen)) {return "";} +void *attmap_add_attributes(void UNUSED(*set),const char UNUSED(*attr)) {return NULL;} +const char *attmap_set_mapping(const char UNUSED(**var),const char UNUSED(*value)) {return NULL;} static void test_isvalidname(void) { diff --git a/tests/test_expr.c b/tests/test_expr.c new file mode 100644 index 0000000..dfc00cb --- /dev/null +++ b/tests/test_expr.c @@ -0,0 +1,146 @@ +/* + test_expr.c - simple tests for the expr module + This file is part of the nss-pam-ldapd library. + + Copyright (C) 2009 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +/* we include expr.c because we want to test the static methods */ +#include "common/expr.c" + +#ifndef __ASSERT_FUNCTION +#define __ASSERT_FUNCTION "" +#endif /* not __ASSERT_FUNCTION */ + +#define assertstreq(str1,str2) \ + (assertstreq_impl(str1,str2,"strcmp(" __STRING(str1) "," __STRING(str2) ")==0", \ + __FILE__, __LINE__, __ASSERT_FUNCTION)) + +/* for Solaris: */ +#define __assert_fail(assertion,file,line,function) __assert(assertion,file,line) + +/* method for determening string equalness */ +static void assertstreq_impl(const char *str1,const char *str2, + const char *assertion,const char *file, + int line,const char *function) +{ + if (strcmp(str1,str2)!=0) + __assert_fail(assertion,file,line,function); +} + +static void test_parse_name(void) +{ + char buffer[20]; + int i; + i=0; + assert(parse_name("fooBar",&i,buffer,sizeof(buffer))!=NULL); + assert(i==6); + i=0; + assert(parse_name("nameThatWillNotFitInBuffer",&i,buffer,sizeof(buffer))==NULL); + i=0; + assert(parse_name("foo Bar",&i,buffer,sizeof(buffer))!=NULL); + assert(i==3); + assertstreq(buffer,"foo"); +} + +static const char *expanderfn(const char *name,void UNUSED(*expander_attr)) +{ + if (strcmp(name,"empty")==0) + return ""; + else + return "foobar"; +} + +static void test_expr_parse(void) +{ + char buffer[1024]; + assert(expr_parse("$test1",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"foobar"); + assert(expr_parse("$empty",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,""); + assert(expr_parse("${test1}\\$",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"foobar$"); + assert(expr_parse("${test1:-default}",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"foobar"); + assert(expr_parse("${empty:-default}",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"default"); + assert(expr_parse("${test1:+setset}",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"setset"); + assert(expr_parse("${empty:+setset}",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,""); + assert(expr_parse("${empty:-$test1}",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"foobar"); + assert(expr_parse("a/$test1/b",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"a/foobar/b"); + assert(expr_parse("a/$empty/b",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"a//b"); + assert(expr_parse("a${test1}b",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"afoobarb"); + assert(expr_parse("a${test1}b${test2:+${test3:-d$test4}e}c",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"afoobarbfoobarec"); + assert(expr_parse("a${test1}b${test2:+${empty:-d$test4}e}c",buffer,sizeof(buffer),expanderfn,NULL)!=NULL); + assertstreq(buffer,"afoobarbdfoobarec"); +} + +static void test_buffer_overflow(void) +{ + char buffer[10]; + assert(expr_parse("$test1$empty$test1",buffer,sizeof(buffer),expanderfn,NULL)==NULL); + assert(expr_parse("long test value",buffer,sizeof(buffer),expanderfn,NULL)==NULL); + assert(expr_parse("${test1:-long test value}",buffer,sizeof(buffer),expanderfn,NULL)==NULL); +} + +static void test_expr_vars(void) +{ + SET *set; + /* simple test */ + set=set_new(); + assert(expr_vars("$a",set)!=NULL); + assert(set_contains(set,"a")); + assert(!set_contains(set,"$a")); + set_free(set); + /* more elaborate test */ + set=set_new(); + assert(expr_vars("\"${gecos:-$cn}\"",set)!=NULL); + assert(set_contains(set,"gecos")); + assert(set_contains(set,"cn")); + set_free(set); + /* more elaborate test */ + set=set_new(); + assert(expr_vars("\"${homeDirectory:-/home/$uidNumber/$uid}\"",set)!=NULL); + assert(set_contains(set,"homeDirectory")); + assert(set_contains(set,"uidNumber")); + assert(set_contains(set,"uid")); + set_free(set); +} + +/* the main program... */ +int main(int UNUSED(argc),char UNUSED(*argv[])) +{ + test_parse_name(); + test_expr_parse(); + test_buffer_overflow(); + test_expr_vars(); + return EXIT_SUCCESS; +} diff --git a/tests/test_myldap.c b/tests/test_myldap.c index 7387d51..44dfc34 100644 --- a/tests/test_myldap.c +++ b/tests/test_myldap.c @@ -2,7 +2,7 @@ test_myldap.c - simple test for the myldap module This file is part of the nss-pam-ldapd library. - Copyright (C) 2007 Arthur de Jong + Copyright (C) 2007, 2008, 2009 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -45,6 +45,9 @@ const char **base_get_var(int UNUSED(map)) {return NULL;} int *scope_get_var(int UNUSED(map)) {return NULL;} const char **filter_get_var(int UNUSED(map)) {return &foo;} const char **attmap_get_var(int UNUSED(map),const char UNUSED(*name)) {return &foo;} +const char *attmap_get_value(MYLDAP_ENTRY UNUSED(*entry),const char UNUSED(*attr),char UNUSED(*buffer),size_t UNUSED(buflen)) {return "";} +void *attmap_add_attributes(void UNUSED(*set),const char UNUSED(*attr)) {return NULL;} +const char *attmap_set_mapping(const char UNUSED(**var),const char UNUSED(*value)) {return NULL;} /* the maxium number of results to print (all results are retrieved) */ #define MAXRESULTS 10 |