/* cfg.c - functions for configuration information This file contains parts that were part of the nss-ldap library which has been forked into the nss-ldapd library. Copyright (C) 1997-2005 Luke Howard Copyright (C) 2007 West Consulting Copyright (C) 2007 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 #include #include #include #include #include #include #include #include "ldap-nss.h" #include "log.h" #include "cfg.h" #include "attmap.h" struct ldap_config *nslcd_cfg=NULL; /* * 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 1 /* seconds to sleep; doubled until max */ #define LDAP_NSS_MAXSLEEPTIME 32 /* maximum seconds to sleep */ #define LDAP_NSS_MAXCONNTRIES 2 /* reconnect attempts before sleeping */ /* the maximum line length in the configuration file */ #define MAX_LINE_LENGTH 4096 /* the maximum number of keywords/options on the line */ #define MAX_LINE_OPTIONS 10 int _nss_ldap_test_config_flag(unsigned int flag) { return nslcd_cfg != NULL && (nslcd_cfg->ldc_flags&flag); } /* clear the configuration information back to the defaults */ static void cfg_defaults(struct ldap_config *cfg) { int i; memset(cfg,0,sizeof(struct ldap_config)); for (i=0;i<(NSS_LDAP_CONFIG_URI_MAX+1);i++) cfg->ldc_uris[i]=NULL; #ifdef LDAP_VERSION3 cfg->ldc_version=LDAP_VERSION3; #else /* LDAP_VERSION3 */ cfg->ldc_version=LDAP_VERSION2; #endif /* not LDAP_VERSION3 */ cfg->ldc_binddn=NULL; cfg->ldc_bindpw=NULL; cfg->ldc_rootbinddn=NULL; cfg->ldc_rootbindpw=NULL; cfg->ldc_saslid=NULL; cfg->ldc_rootsaslid=NULL; cfg->ldc_sasl_secprops=NULL; cfg->ldc_usesasl=0; cfg->ldc_rootusesasl=0; #ifdef CONFIGURE_KRB5_CCNAME cfg->ldc_krb5_ccname=NULL; #endif /* CONFIGURE_KRB5_CCNAME */ cfg->ldc_base=NULL; cfg->ldc_scope=LDAP_SCOPE_SUBTREE; cfg->ldc_deref=LDAP_DEREF_NEVER; cfg->ldc_referrals=1; cfg->ldc_timelimit=LDAP_NO_LIMIT; cfg->ldc_bind_timelimit=30; cfg->ldc_reconnect_pol=LP_RECONNECT_HARD_OPEN; cfg->ldc_flags=0; cfg->ldc_idle_timelimit=0; cfg->ldc_ssl_on=SSL_OFF; cfg->ldc_sslpath=NULL; cfg->ldc_tls_checkpeer=-1; cfg->ldc_tls_cacertdir=NULL; cfg->ldc_tls_cacertfile=NULL; cfg->ldc_tls_randfile=NULL; cfg->ldc_tls_ciphers=NULL; cfg->ldc_tls_cert=NULL; cfg->ldc_tls_key=NULL; cfg->ldc_restart=1; cfg->ldc_pagesize=0; cfg->ldc_reconnect_tries=LDAP_NSS_TRIES; cfg->ldc_reconnect_sleeptime=LDAP_NSS_SLEEPTIME; cfg->ldc_reconnect_maxsleeptime=LDAP_NSS_MAXSLEEPTIME; cfg->ldc_debug=0; cfg->ldc_password_type=LU_RFC2307_USERPASSWORD; cfg->ldc_shadow_type=LS_RFC2307_SHADOW; } /* simple strdup wrapper */ static char *xstrdup(const char *s) { char *tmp; if (s==NULL) { log_log(LOG_CRIT,"xstrdup() called with NULL"); exit(EXIT_FAILURE); } tmp=strdup(s); if (tmp==NULL) { log_log(LOG_CRIT,"strdup() failed to allocate memory"); exit(EXIT_FAILURE); } return tmp; } /* add a single URI to the list of URIs in the configuration */ static void add_uri(const char *filename,int lnr, struct ldap_config *cfg,const char *uri) { int i; log_log(LOG_DEBUG,"add_uri(%s)",uri); /* find the place where to insert the URI */ for (i=0;cfg->ldc_uris[i]!=NULL;i++) ; /* check for room */ if (i>=NSS_LDAP_CONFIG_URI_MAX) { log_log(LOG_ERR,"%s:%d: maximum number of URIs exceeded",filename,lnr); exit(EXIT_FAILURE); } /* append URI to list */ cfg->ldc_uris[i]=xstrdup(uri); cfg->ldc_uris[i+1]=NULL; } static int parse_boolean(const char *filename,int lnr,const char *value) { if ( (strcasecmp(value,"on")==0) || (strcasecmp(value,"yes")==0) || (strcasecmp(value,"true")==0) || (strcasecmp(value,"1")==0) ) return 1; else if ( (strcasecmp(value,"off")==0) || (strcasecmp(value,"no")==0) || (strcasecmp(value,"false")==0) || (strcasecmp(value,"0")==0) ) return 0; else { log_log(LOG_ERR,"%s:%d: not a boolean argument: '%s'",filename,lnr,value); exit(EXIT_FAILURE); } } static int parse_scope(const char *filename,int lnr,const char *value) { if ( (strcasecmp(value,"sub")==0) || (strcasecmp(value,"subtree")==0) ) return LDAP_SCOPE_SUBTREE; else if ( (strcasecmp(value,"one")==0) || (strcasecmp(value,"onelevel")==0) ) return LDAP_SCOPE_ONELEVEL; else if (strcasecmp(value,"base")==0) return LDAP_SCOPE_BASE; else { log_log(LOG_ERR,"%s:%d: not a scope argument: '%s'",filename,lnr,value); exit(EXIT_FAILURE); } } static enum ldap_map_selector parse_map(const char *filename,int lnr,const char *value) { if ( (strcasecmp(value,"alias")==0) || (strcasecmp(value,"aliases")==0) ) return LM_ALIASES; else if ( (strcasecmp(value,"ether")==0) || (strcasecmp(value,"ethers")==0) ) return LM_ETHERS; else if (strcasecmp(value,"group")==0) return LM_GROUP; else if ( (strcasecmp(value,"host")==0) || (strcasecmp(value,"hosts")==0) ) return LM_HOSTS; else if (strcasecmp(value,"netgroup")==0) return LM_NETGROUP; else if ( (strcasecmp(value,"network")==0) || (strcasecmp(value,"networks")==0) ) return LM_NETWORKS; else if (strcasecmp(value,"passwd")==0) return LM_PASSWD; else if ( (strcasecmp(value,"protocol")==0) || (strcasecmp(value,"protocols")==0) ) return LM_PROTOCOLS; else if (strcasecmp(value,"rpc")==0) return LM_RPC; else if ( (strcasecmp(value,"service")==0) || (strcasecmp(value,"services")==0) ) return LM_SERVICES; else if (strcasecmp(value,"shadow")==0) return LM_SHADOW; else { log_log(LOG_ERR,"%s:%d: unknown mapping: '%s'",filename,lnr,value); exit(EXIT_FAILURE); } } /* check that the condition is true and otherwise log an error and bail out */ static inline void check_argumentcount(const char *filename,int lnr, const char *keyword,int condition) { if (!condition) { log_log(LOG_ERR,"%s:%d: %s: wrong number of arguments",filename,lnr,keyword); exit(EXIT_FAILURE); } } static void parse_base_statement(const char *filename,int lnr, const char **opts,int nopts, struct ldap_config *cfg) { enum ldap_map_selector map; const char **var; if (nopts==2) cfg->ldc_base=xstrdup(opts[1]); else if (nopts==3) { /* get the map */ map=parse_map(filename,lnr,opts[1]); /* get the base variable to set */ var=base_get_var(map); if (var==NULL) { log_log(LOG_ERR,"%s:%d: unknown map: '%s'",filename,lnr,opts[1]); exit(EXIT_FAILURE); } /* check if the value will be changed */ if ((*var==NULL)||(strcmp(*var,opts[2])!=0)) { /* Note: we have a memory leak here if a single mapping is changed multiple times in one config (deemed not a problem) */ *var=xstrdup(opts[2]); } } else check_argumentcount(filename,lnr,opts[0],0); } static void parse_scope_statement(const char *filename,int lnr, const char **opts,int nopts, struct ldap_config *cfg) { enum ldap_map_selector map; int *var; if (nopts==2) cfg->ldc_scope=parse_scope(filename,lnr,opts[1]); else if (nopts==3) { /* get the map */ map=parse_map(filename,lnr,opts[1]); /* get the scope variable to set */ var=scope_get_var(map); if (var==NULL) { log_log(LOG_ERR,"%s:%d: unknown map: '%s'",filename,lnr,opts[1]); exit(EXIT_FAILURE); } /* set the scope */ *var=parse_scope(filename,lnr,opts[2]); } else check_argumentcount(filename,lnr,opts[0],0); } static void parse_filter_statement(const char *filename,int lnr, const char **opts,int nopts) { enum ldap_map_selector map; const char **var; check_argumentcount(filename,lnr,opts[0],nopts==3); /* get the map */ map=parse_map(filename,lnr,opts[1]); /* get the filter variable to set */ var=filter_get_var(map); if (var==NULL) { log_log(LOG_ERR,"%s:%d: unknown map: '%s'",filename,lnr,opts[1]); exit(EXIT_FAILURE); } /* check if the value will be changed */ if (strcmp(*var,opts[2])!=0) { /* Note: we have a memory leak here if a single mapping is changed multiple times in one config (deemed not a problem) */ *var=xstrdup(opts[2]); } } /* this function modifies the statement argument passed */ static void parse_map_statement(const char *filename,int lnr, const char **opts,int nopts, struct ldap_config *cfg) { enum ldap_map_selector map; const char **var; check_argumentcount(filename,lnr,opts[0],nopts==4); /* get the map */ map=parse_map(filename,lnr,opts[1]); /* special handling for some attribute mappings */ /* TODO: move this stuff to passwd.c or shadow.c or wherever it's used */ if ((map==LM_PASSWD)&&(strcasecmp(opts[2],"userPassword")==0)) { if (strcasecmp(opts[3],"userPassword")==0) cfg->ldc_password_type=LU_RFC2307_USERPASSWORD; else if (strcasecmp(opts[3],"authPassword")==0) cfg->ldc_password_type=LU_RFC3112_AUTHPASSWORD; else cfg->ldc_password_type=LU_OTHER_PASSWORD; } else if ((map==LM_SHADOW)&&(strcasecmp(opts[2],"shadowLastChange")==0)) { if (strcasecmp(opts[3],"shadowLastChange")==0) cfg->ldc_shadow_type=LS_RFC2307_SHADOW; else if (strcasecmp(opts[3],"pwdLastSet")==0) cfg->ldc_shadow_type=LS_AD_SHADOW; } /* get the attribute variable to set */ var=attmap_get_var(map,opts[2]); if (var==NULL) { log_log(LOG_ERR,"%s:%d: unknown attribute to map: '%s'",filename,lnr,opts[2]); exit(EXIT_FAILURE); } /* check if the value will be changed */ if (strcmp(*var,opts[3])!=0) { /* Note: we have a memory leak here if a single mapping is changed multiple times in one config (deemed not a problem) */ *var=xstrdup(opts[3]); } } /* split a line from the configuration file note that this code is not thread safe since a pointer to the same storage will be returned with each call the line string is modified */ static const char **tokenize(const char *filename,int lnr,char *line,int *nopt) { static const char *retv[MAX_LINE_OPTIONS]; int opt; for (opt=0;opt1); for (i=1;ildc_version=atoi(opts[1]); } else if (strcasecmp(opts[0],"binddn")==0) { check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_binddn=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"bindpw")==0) { check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_bindpw=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"rootbinddn")==0) { log_log(LOG_ERR,"%s:%d: option %s is currently unsupported",filename,lnr,opts[0]); exit(EXIT_FAILURE); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_rootbinddn=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"rootbindpw")==0) { log_log(LOG_ERR,"%s:%d: option %s is currently unsupported",filename,lnr,opts[0]); exit(EXIT_FAILURE); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_rootbindpw=xstrdup(opts[1]); } /* SASL authentication options */ else if (strcasecmp(opts[0], "sasl_authid")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_saslid=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"rootsasl_authid")==0) { log_log(LOG_ERR,"%s:%d: option %s is currently unsupported",filename,lnr,opts[0]); exit(EXIT_FAILURE); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_rootsaslid=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"sasl_secprops")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_sasl_secprops=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"use_sasl")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_usesasl=parse_boolean(filename,lnr,opts[1]); } else if (strcasecmp(opts[0],"rootuse_sasl")==0) { log_log(LOG_ERR,"%s:%d: option %s is currently unsupported",filename,lnr,opts[0]); exit(EXIT_FAILURE); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_rootusesasl=parse_boolean(filename,lnr,opts[1]); } #ifdef CONFIGURE_KRB5_CCNAME /* Kerberos authentication options */ else if (strcasecmp(opts[0],"krb5_ccname")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_krb5_ccname=xstrdup(opts[1]); } #endif /* CONFIGURE_KRB5_CCNAME */ /* search/mapping options */ else if (strcasecmp(opts[0],"base")==0) { parse_base_statement(filename,lnr,opts,nopts,cfg); } else if (strcasecmp(opts[0],"scope")==0) { parse_scope_statement(filename,lnr,opts,nopts,cfg); } else if (strcasecmp(opts[0],"deref")==0) { check_argumentcount(filename,lnr,opts[0],nopts==2); if (strcasecmp(opts[1],"never")==0) cfg->ldc_deref=LDAP_DEREF_NEVER; else if (strcasecmp(opts[1],"searching")==0) cfg->ldc_deref=LDAP_DEREF_SEARCHING; else if (strcasecmp(opts[1],"finding")==0) cfg->ldc_deref=LDAP_DEREF_FINDING; else if (strcasecmp(opts[1],"always")==0) cfg->ldc_deref=LDAP_DEREF_ALWAYS; else { log_log(LOG_ERR,"%s:%d: wrong argument: '%s'",filename,lnr,opts[1]); exit(EXIT_FAILURE); } } else if (strcasecmp(opts[0],"referrals")==0) { check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_referrals=parse_boolean(filename,lnr,opts[1]); } else if (strcasecmp(opts[0],"filter")==0) { parse_filter_statement(filename,lnr,opts,nopts); } else if (strcasecmp(opts[0],"map")==0) { parse_map_statement(filename,lnr,opts,nopts,cfg); } /* timing/reconnect options */ else if (strcasecmp(opts[0],"timelimit")==0) { check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_timelimit=atoi(opts[1]); } else if (strcasecmp(opts[0],"bind_timelimit")==0) { check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_bind_timelimit=atoi(opts[1]); } else if (strcasecmp(opts[0],"bind_policy")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (and may be removed in an upcoming release)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); if ( (strcasecmp(opts[1],"hard")==0) || (strcasecmp(opts[1],"hard_open")==0) ) cfg->ldc_reconnect_pol=LP_RECONNECT_HARD_OPEN; else if (strcasecmp(opts[1],"hard_init")==0) cfg->ldc_reconnect_pol=LP_RECONNECT_HARD_INIT; else if (strcasecmp(opts[1],"soft")==0) cfg->ldc_reconnect_pol=LP_RECONNECT_SOFT; } else if (strcasecmp(opts[0],"nss_connect_policy")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (and may be removed in an upcoming release)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); if (!strcasecmp(opts[1],"oneshot")) cfg->ldc_flags|=NSS_LDAP_FLAGS_CONNECT_POLICY_ONESHOT; else if (!strcasecmp(opts[1],"persist")) cfg->ldc_flags&=~(NSS_LDAP_FLAGS_CONNECT_POLICY_ONESHOT); } else if (strcasecmp(opts[0],"idle_timelimit")==0) { check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_idle_timelimit=atoi(opts[1]); } /* SSL/TLS options */ else if (strcasecmp(opts[0],"ssl")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); if (strcasecmp(opts[1],"start_tls")==0) cfg->ldc_ssl_on=SSL_START_TLS; else if (parse_boolean(filename,lnr,opts[1])) cfg->ldc_ssl_on=SSL_LDAPS; } else if (strcasecmp(opts[0],"sslpath")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_sslpath=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"tls_checkpeer")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_tls_checkpeer=parse_boolean(filename,lnr,opts[1]); } else if (strcasecmp(opts[0],"tls_cacertdir")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_tls_cacertdir=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"tls_cacertfile")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_tls_cacertfile=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"tls_randfile")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_tls_randfile=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"tls_ciphers")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_tls_ciphers=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"tls_cert")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_tls_cert=xstrdup(opts[1]); } else if (strcasecmp(opts[0],"tls_key")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (please report any successes)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_tls_key=xstrdup(opts[1]); } /* other options */ else if (strcasecmp(opts[0],"restart")==0) { log_log(LOG_WARNING,"%s:%d: option %s is currently untested (and may be removed in an upcoming release)",filename,lnr,opts[0]); check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_restart=parse_boolean(filename,lnr,opts[1]); } else if (strcasecmp(opts[0],"pagesize")==0) { check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_pagesize=atoi(opts[1]); } /* undocumented options */ else if (strcasecmp(opts[0],"nss_reconnect_tries")==0) { check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_reconnect_tries=atoi(opts[1]); } else if (!strcasecmp(opts[0],"nss_reconnect_sleeptime")) { check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_reconnect_sleeptime=atoi(opts[1]); } else if (strcasecmp(opts[0],"nss_reconnect_maxsleeptime")==0) { check_argumentcount(filename,lnr,opts[0],nopts==2); cfg->ldc_reconnect_maxsleeptime=atoi(opts[1]); } else { log_log(LOG_ERR,"%s:%d: unknown keyword: '%s'",filename,lnr,opts[0]); exit(EXIT_FAILURE); } } /* we're done reading file, close */ fclose(fp); } void cfg_init(const char *fname) { if (nslcd_cfg==NULL) { /* allocate the memory */ nslcd_cfg=(struct ldap_config *)malloc(sizeof(struct ldap_config)); if (nslcd_cfg==NULL) { log_log(LOG_CRIT,"malloc() failed to allocate memory"); exit(EXIT_FAILURE); } /* clear configuration */ cfg_defaults(nslcd_cfg); /* read configfile */ cfg_read(fname,nslcd_cfg); /* do some sanity checks */ if (nslcd_cfg->ldc_uris[0] == NULL) { log_log(LOG_ERR,"no URIs defined in config"); exit(EXIT_FAILURE); } } }