~ubuntu-branches/ubuntu/precise/nss-pam-ldapd/precise-security

« back to all changes in this revision

Viewing changes to nslcd/cfg.c

  • Committer: Package Import Robot
  • Author(s): Arthur de Jong
  • Date: 2011-09-04 21:00:00 UTC
  • mfrom: (14.1.4 experimental)
  • Revision ID: package-import@ubuntu.com-20110904210000-pe3u91iga88vtr16
Tags: 0.8.4
* Upload to unstable
* switch to using the member attribute by default instead of
  uniqueMember (backwards incompatible change)
* only return "x" as a password hash when the object has the shadowAccount
  objectClass and nsswitch.conf is configured to do shadow lookups using
  LDAP (this avoids some problems with pam_unix)
* fix problem with partial attribute name matches in DN (thanks Timothy
  White)
* fix a problem with objectSid mappings with recent versions of OpenLDAP
  (patch by Wesley Mason)
* set the socket timeout in a connection callback to avoid timeout
  issues during the SSL handshake (patch by Stefan Völkel)
* check for unknown variables in pam_authz_search
* only check password expiration when authenticating, only check account
  expiration when doing authorisation
* make buffer sizes consistent and grow all buffers holding string
  representations of numbers to be able to hold 64-bit numbers
* update AX_PTHREAD from autoconf-archive
* support querying DNS SRV records from a different domain than the current
  one (based on a patch by James M. Leddy)
* fix a problem with uninitialised memory while parsing the tls_ciphers
  option (closes: #638872) (but doesn't work yet due to #640384)
* implement bounds checking of numeric values read from LDAP (patch by
  Jakub Hrozek)
* correctly support large uid and gid values from LDAP (patch by Jakub
  Hrozek)
* improvements to the configure script (patch by Jakub Hrozek)
* switch to dh for debian/rules and bump debhelper compatibility to 8
* build Debian packages with multiarch support
* ship shlibs (but still no symbol files) for libnss-ldapd since that was
  the easiest way to support multiarch
* fix output in init script when restarting nslcd (closes: #637132)
* correctly handle leading and trailing spaces in preseeded debconf uri
  option (patch by Andreas B. Mundt) (closes: #637863)
* support spaces around database names in /etc/nsswitch.conf while
  configuring package (closes: #640185)
* updated Russian debconf translation by Yuri Kozlov (closes: #637751)
* updated French debconf translation by Christian Perrier (closes: #637756)
* added Slovak debconf translation by Slavko (closes: #637759)
* updated Danish debconf translation by Joe Hansen (closes :#637763)
* updated Brazilian Portuguese debconf translation by Denis Doria
* updated Portuguese debconf translation by Américo Monteiro
* updated Japanese debconf translation by Kenshi Muto (closes: #638195)
* updated Czech debconf translation by Miroslav Kure (closes: #639026)
* updated German debconf translation by Chris Leick (closes: #639107)
* updated Spanish debconf translation by Francisco Javier Cuadrado
  (closes: #639236)
* updated Dutch debconf translation by Arthur de Jong with help from Paul
  Gevers and Jeroen Schot

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
 
6
6
   Copyright (C) 1997-2005 Luke Howard
7
7
   Copyright (C) 2007 West Consulting
8
 
   Copyright (C) 2007, 2008, 2009, 2010 Arthur de Jong
 
8
   Copyright (C) 2007, 2008, 2009, 2010, 2011 Arthur de Jong
9
9
 
10
10
   This library is free software; you can redistribute it and/or
11
11
   modify it under the terms of the GNU Lesser General Public
57
57
#include "log.h"
58
58
#include "cfg.h"
59
59
#include "attmap.h"
 
60
#include "common/expr.h"
60
61
 
61
62
struct ldap_config *nslcd_cfg=NULL;
62
63
 
75
76
    exit(EXIT_FAILURE); \
76
77
  }
77
78
 
 
79
/* prototype for parse_validnames_statement() because it is used in
 
80
   cfg_defaults() */
 
81
static void parse_validnames_statement(const char *filename,int lnr,
 
82
                   const char *keyword,char *line,struct ldap_config *cfg);
 
83
 
78
84
/* set the configuration information to the defaults */
79
85
static void cfg_defaults(struct ldap_config *cfg)
80
86
{
97
103
  cfg->ldc_binddn=NULL;
98
104
  cfg->ldc_bindpw=NULL;
99
105
  cfg->ldc_rootpwmoddn=NULL;
 
106
  cfg->ldc_rootpwmodpw=NULL;
100
107
  cfg->ldc_sasl_mech=NULL;
101
108
  cfg->ldc_sasl_realm=NULL;
102
109
  cfg->ldc_sasl_authcid=NULL;
119
126
  cfg->ldc_pagesize=0;
120
127
  cfg->ldc_nss_initgroups_ignoreusers=NULL;
121
128
  cfg->ldc_pam_authz_search=NULL;
 
129
  cfg->ldc_nss_min_uid=0;
 
130
  parse_validnames_statement(__FILE__,__LINE__,"",
 
131
                "/^[a-z0-9._@$][a-z0-9._@$ \\~-]*[a-z0-9._@$~-]$/i",cfg);
122
132
}
123
133
 
124
134
/* simple strdup wrapper */
158
168
  cfg->ldc_uris[i].uri=xstrdup(uri);
159
169
}
160
170
 
161
 
#ifndef HOST_NAME_MAX
162
 
#define HOST_NAME_MAX 255
163
 
#endif /* not HOST_NAME_MAX */
164
 
 
165
171
#ifdef HAVE_LDAP_DOMAIN2HOSTLIST
166
172
/* return the domain name of the current host
167
173
   the returned string must be freed by caller */
168
 
static char *cfg_getdomainname(const char *filename,int lnr)
 
174
static const char *cfg_getdomainname(const char *filename,int lnr)
169
175
{
170
 
  char hostname[HOST_NAME_MAX+1],*domain;
171
 
  int hostnamelen;
172
 
  int i;
173
 
  struct hostent *host=NULL;
174
 
  /* get system hostname */
175
 
  if (gethostname(hostname,sizeof(hostname))<0)
176
 
  {
177
 
    log_log(LOG_ERR,"%s:%d: gethostname() failed: %s",filename,lnr,strerror(errno));
178
 
    exit (EXIT_FAILURE);
179
 
  }
180
 
  hostnamelen=strlen(hostname);
181
 
  /* lookup hostent */
182
 
  host=gethostbyname(hostname);
183
 
  if (host==NULL)
184
 
  {
185
 
    log_log(LOG_ERR,"%s:%d: gethostbyname(%s): %s",filename,lnr,hostname,hstrerror(h_errno));
186
 
    exit(EXIT_FAILURE);
187
 
  }
188
 
  /* check h_name for fqdn starting with our hostname */
189
 
  if ((strncasecmp(hostname,host->h_name,hostnamelen)==0)&&
190
 
      (host->h_name[hostnamelen]=='.')&&
191
 
      (host->h_name[hostnamelen+1]!='\0'))
192
 
    return strdup(host->h_name+hostnamelen+1);
193
 
  /* also check h_aliases */
194
 
  for (i=0;host->h_aliases[i]!=NULL;i++)
195
 
  {
196
 
    if ((strncasecmp(hostname,host->h_aliases[i],hostnamelen)==0)&&
197
 
        (host->h_aliases[i][hostnamelen]=='.')&&
198
 
        (host->h_aliases[i][hostnamelen+1]!='\0'))
199
 
      return strdup(host->h_aliases[i]+hostnamelen+1);
200
 
  }
201
 
  /* fall back to any domain part in h_name */
202
 
  if (((domain=strchr(host->h_name,'.'))!=NULL)&&
203
 
      (domain[1]!='\0'))
204
 
    return strdup(domain+1);
205
 
  /* also check h_aliases */
206
 
  for (i=0;host->h_aliases[i]!=NULL;i++)
207
 
  {
208
 
    if (((domain=strchr(host->h_aliases[i],'.'))!=NULL)&&
209
 
        (domain[1]!='\0'))
210
 
      return strdup(domain+1);
211
 
  }
212
 
  /* we've tried everything now */
213
 
  log_log(LOG_ERR,"%s:%d: unable to determinate a domainname for hostname %s",
214
 
          filename,lnr,hostname);
 
176
  const char *fqdn,*domain;
 
177
  fqdn=getfqdn();
 
178
  if ((fqdn!=NULL)&&((domain=strchr(fqdn,'.'))!=NULL)&&(domain[1]!='\0'))
 
179
    return domain+1;
 
180
  log_log(LOG_ERR,"%s:%d: unable to determinate a domain name",
 
181
          filename,lnr);
215
182
  exit(EXIT_FAILURE);
216
183
}
217
184
 
218
185
/* add URIs by doing DNS queries for SRV records */
219
186
static void add_uris_from_dns(const char *filename,int lnr,
220
 
                              struct ldap_config *cfg)
 
187
                              struct ldap_config *cfg,
 
188
                              const char *domain)
221
189
{
222
 
  int ret=0;
223
 
  char *domain;
 
190
  int rc;
224
191
  char *hostlist=NULL,*nxt;
225
192
  char buf[HOST_NAME_MAX+sizeof("ldap://")];
226
 
  domain=cfg_getdomainname(filename,lnr);
227
 
  ret=ldap_domain2hostlist(domain,&hostlist);
 
193
  log_log(LOG_DEBUG,"query %s for SRV records",domain);
 
194
  rc=ldap_domain2hostlist(domain,&hostlist);
228
195
  /* FIXME: have better error handling */
229
196
  if ((hostlist==NULL)||(*hostlist=='\0'))
230
197
  {
249
216
    /* get next entry from list */
250
217
    hostlist=nxt;
251
218
  }
252
 
  free(domain);
253
219
}
254
220
#endif /* HAVE_LDAP_DOMAIN2HOSTLIST */
255
221
 
377
343
  }
378
344
}
379
345
 
 
346
/* check that the file is not world readable */
 
347
static void check_permissions(const char *filename,const char *keyword)
 
348
{
 
349
  struct stat sb;
 
350
  /* get file status */
 
351
  if (stat(filename,&sb))
 
352
  {
 
353
    log_log(LOG_ERR,"cannot stat() %s: %s",filename,strerror(errno));
 
354
    exit(EXIT_FAILURE);
 
355
  }
 
356
  /* check permissions */
 
357
  if ((sb.st_mode&0007)!=0)
 
358
  {
 
359
    if (keyword!=NULL)
 
360
      log_log(LOG_ERR,"%s: file should not be world readable if %s is set",
 
361
              filename, keyword);
 
362
    else
 
363
      log_log(LOG_ERR,"%s: file should not be world readable",filename);
 
364
    exit(EXIT_FAILURE);
 
365
  }
 
366
}
 
367
 
380
368
static void get_int(const char *filename,int lnr,
381
369
                    const char *keyword,char **line,
382
370
                    int *var)
415
403
                        char **var)
416
404
{
417
405
  check_argumentcount(filename,lnr,keyword,(*line!=NULL)&&(**line!='\0'));
418
 
  if ((*var==NULL)||(strcmp(*var,*line)!=0))
419
 
  {
420
 
    /* Note: we have a memory leak here if a single mapping is changed
421
 
             multiple times in one config (deemed not a problem) */
422
 
    *var=xstrdup(*line);
423
 
  }
 
406
  /* Note: we have a memory leak here if a single mapping is changed
 
407
           multiple times in one config (deemed not a problem) */
 
408
  *var=xstrdup(*line);
 
409
  /* mark that we are at the end of the line */
424
410
  *line=NULL;
425
411
}
426
412
 
444
430
  char *tmp;
445
431
  check_argumentcount(filename,lnr,keyword,get_token(line,token,sizeof(token))!=NULL);
446
432
  /* check if it is a valid numerical uid */
447
 
  *var=(uid_t)strtol(token,&tmp,0);
448
 
  if ((*token!='\0')&&(*tmp=='\0'))
 
433
  errno=0;
 
434
  *var=strtouid(token,&tmp,0);
 
435
  if ((*token!='\0')&&(*tmp=='\0')&&(errno==0))
449
436
    return;
450
437
  /* find by name */
451
438
  pwent=getpwnam(token);
469
456
  char *tmp;
470
457
  check_argumentcount(filename,lnr,keyword,get_token(line,token,sizeof(token))!=NULL);
471
458
  /* check if it is a valid numerical gid */
472
 
  *var=(gid_t)strtol(token,&tmp,0);
473
 
  if ((*token!='\0')&&(*tmp=='\0'))
 
459
  errno=0;
 
460
  *var=strtogid(token,&tmp,0);
 
461
  if ((*token!='\0')&&(*tmp=='\0')&&(errno==0))
474
462
    return;
475
463
  /* find by name */
476
464
  grent=getgrnam(token);
567
555
                     const char *value,const char **var)
568
556
{
569
557
#ifdef HAVE_LDAP_DOMAIN2DN
570
 
  char *domain = NULL;
 
558
  const char *domain=NULL;
571
559
  char *domaindn=NULL;
572
560
#endif /* HAVE_LDAP_DOMAIN2DN */
573
561
  /* if the base is "DOMAIN" use the domain name */
576
564
#ifdef HAVE_LDAP_DOMAIN2DN
577
565
    domain=cfg_getdomainname(filename,lnr);
578
566
    ldap_domain2dn(domain,&domaindn);
579
 
    free(domain);
580
567
    log_log(LOG_DEBUG,"set_base(): setting base to %s from domain",domaindn);
581
568
    value=domaindn;
582
569
#else /* not HAVE_LDAP_DOMAIN2DN */
588
575
  *var=xstrdup(value);
589
576
}
590
577
 
 
578
/* parse the validnames statement */
 
579
static void parse_validnames_statement(const char *filename,int lnr,
 
580
                   const char *keyword,char *line,struct ldap_config *cfg)
 
581
{
 
582
  char *value;
 
583
  int i,l;
 
584
  int flags=REG_EXTENDED|REG_NOSUB;
 
585
  /* the rest of the line should be a regular expression */
 
586
  get_restdup(filename,lnr,keyword,&line,&value);
 
587
  /* check formatting and update flags */
 
588
  if (value[0]!='/')
 
589
  {
 
590
    log_log(LOG_ERR,"%s:%d: regular expression incorrectly delimited",filename,lnr);
 
591
    exit(EXIT_FAILURE);
 
592
  }
 
593
  l=strlen(value);
 
594
  if (value[l-1]=='i')
 
595
  {
 
596
    value[l-1]='\0';
 
597
    l--;
 
598
    flags|=REG_ICASE;
 
599
  }
 
600
  if (value[l-1]!='/')
 
601
  {
 
602
    log_log(LOG_ERR,"%s:%d: regular expression incorrectly delimited",filename,lnr);
 
603
    exit(EXIT_FAILURE);
 
604
  }
 
605
  value[l-1]='\0';
 
606
  /* compile the regular expression */
 
607
  if ((i=regcomp(&cfg->validnames,value+1,flags))!= 0)
 
608
  {
 
609
    /* get the error message */
 
610
    l=regerror(i,&cfg->validnames,NULL,0);
 
611
    value=malloc(l);
 
612
    if (value==NULL)
 
613
      log_log(LOG_ERR,"%s:%d: invalid regular expression",filename,lnr);
 
614
    else
 
615
    {
 
616
      regerror(i,&cfg->validnames,value,l);
 
617
      log_log(LOG_ERR,"%s:%d: invalid regular expression: %s",filename,lnr,
 
618
              value);
 
619
    }
 
620
    exit(EXIT_FAILURE);
 
621
  }
 
622
}
 
623
 
591
624
static void parse_base_statement(const char *filename,int lnr,
592
625
                                 const char *keyword,char *line,
593
626
                                 struct ldap_config *cfg)
724
757
  }
725
758
}
726
759
 
 
760
static void parse_pam_authz_search_statement(
 
761
              const char *filename,int lnr,const char *keyword,
 
762
              char *line,struct ldap_config *cfg)
 
763
{
 
764
  SET *set;
 
765
  const char **list;
 
766
  int i;
 
767
  check_argumentcount(filename,lnr,keyword,(line!=NULL)&&(*line!='\0'));
 
768
  cfg->ldc_pam_authz_search=xstrdup(line);
 
769
  /* check the variables used in the expression */
 
770
  set=expr_vars(cfg->ldc_pam_authz_search,NULL);
 
771
  list=set_tolist(set);
 
772
  for (i=0;list[i]!=NULL;i++)
 
773
  {
 
774
    if ((strcmp(list[i],"username")!=0)&&
 
775
        (strcmp(list[i],"service")!=0)&&
 
776
        (strcmp(list[i],"ruser")!=0)&&
 
777
        (strcmp(list[i],"rhost")!=0)&&
 
778
        (strcmp(list[i],"tty")!=0)&&
 
779
        (strcmp(list[i],"hostname")!=0)&&
 
780
        (strcmp(list[i],"fqdn")!=0)&&
 
781
        (strcmp(list[i],"dn")!=0)&&
 
782
        (strcmp(list[i],"uid")!=0))
 
783
    {
 
784
      log_log(LOG_ERR,"%s:%d: unknown variable $%s",filename,lnr,list[i]);
 
785
      exit(EXIT_FAILURE);
 
786
    }
 
787
  }
 
788
  /* free memory */
 
789
  set_free(set);
 
790
  free(list);
 
791
}
 
792
 
727
793
static void cfg_read(const char *filename,struct ldap_config *cfg)
728
794
{
729
795
  FILE *fp;
790
856
        if (strcasecmp(token,"dns")==0)
791
857
        {
792
858
#ifdef HAVE_LDAP_DOMAIN2HOSTLIST
793
 
          add_uris_from_dns(filename,lnr,cfg);
 
859
          add_uris_from_dns(filename,lnr,cfg,cfg_getdomainname(filename,lnr));
 
860
#else /* not HAVE_LDAP_DOMAIN2HOSTLIST */
 
861
          log_log(LOG_ERR,"%s:%d: value %s not supported on platform",filename,lnr,token);
 
862
          exit(EXIT_FAILURE);
 
863
#endif /* not HAVE_LDAP_DOMAIN2HOSTLIST */
 
864
        }
 
865
        else if (strncasecmp(token,"dns:",4)==0)
 
866
        {
 
867
#ifdef HAVE_LDAP_DOMAIN2HOSTLIST
 
868
          add_uris_from_dns(filename,lnr,cfg,strdup(token+sizeof("dns")));
794
869
#else /* not HAVE_LDAP_DOMAIN2HOSTLIST */
795
870
          log_log(LOG_ERR,"%s:%d: value %s not supported on platform",filename,lnr,token);
796
871
          exit(EXIT_FAILURE);
811
886
    }
812
887
    else if (strcasecmp(keyword,"bindpw")==0)
813
888
    {
 
889
      check_permissions(filename,keyword);
814
890
      get_restdup(filename,lnr,keyword,&line,&cfg->ldc_bindpw);
815
891
    }
816
892
    else if (strcasecmp(keyword,"rootpwmoddn")==0)
817
893
    {
818
894
      get_restdup(filename,lnr,keyword,&line,&cfg->ldc_rootpwmoddn);
819
895
    }
 
896
    else if (strcasecmp(keyword,"rootpwmodpw")==0)
 
897
    {
 
898
      check_permissions(filename,keyword);
 
899
      get_restdup(filename,lnr,keyword,&line,&cfg->ldc_rootpwmodpw);
 
900
    }
820
901
    /* SASL authentication options */
821
902
    else if (strcasecmp(keyword,"use_sasl")==0)
822
903
    {
956
1037
      LDAP_SET_OPTION(NULL,LDAP_OPT_X_TLS_CACERTDIR,value);
957
1038
      free(value);
958
1039
    }
959
 
    else if (strcasecmp(keyword,"tls_cacertfile")==0)
 
1040
    else if ( (strcasecmp(keyword,"tls_cacertfile")==0) ||
 
1041
              (strcasecmp(keyword,"tls_cacert")==0) )
960
1042
    {
961
1043
      get_strdup(filename,lnr,keyword,&line,&value);
962
1044
      get_eol(filename,lnr,keyword,&line);
1018
1100
    }
1019
1101
    else if (strcasecmp(keyword,"pam_authz_search")==0)
1020
1102
    {
1021
 
      check_argumentcount(filename,lnr,keyword,(line!=NULL)&&(*line!='\0'));
1022
 
      cfg->ldc_pam_authz_search=xstrdup(line);
 
1103
      parse_pam_authz_search_statement(filename,lnr,keyword,line,cfg);
 
1104
    }
 
1105
    else if (strcasecmp(keyword,"nss_min_uid")==0)
 
1106
    {
 
1107
      get_uid(filename,lnr,keyword,&line,&cfg->ldc_nss_min_uid);
 
1108
      get_eol(filename,lnr,keyword,&line);
 
1109
    }
 
1110
    else if (strcasecmp(keyword,"validnames")==0)
 
1111
    {
 
1112
      parse_validnames_statement(filename,lnr,keyword,line,cfg);
1023
1113
    }
1024
1114
#ifdef ENABLE_CONFIGFILE_CHECKING
1025
1115
    /* fallthrough */
1055
1145
      exit(EXIT_FAILURE);
1056
1146
    }
1057
1147
  }
 
1148
  /* check permissions */
 
1149
  check_permissions(filename,NULL);
1058
1150
  /* read the first line */
1059
1151
  if (fgets(linebuf,sizeof(linebuf),fp)==NULL)
1060
1152
  {
1062
1154
    exit(EXIT_FAILURE);
1063
1155
  }
1064
1156
  /* chop the last char off and save the rest as bindpw */
1065
 
  i=strlen(linebuf);
1066
 
 
1067
1157
  i=(int)strlen(linebuf);
1068
1158
  if ((i<=0)||(linebuf[i-1]!='\n'))
1069
1159
  {