/* Copyright 2000, 2001, 2002, 2003 Laurent Wacrenier This file is part of libhome libhome 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 of the License, or (at your option) any later version. libhome 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 libhome; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" static char const rcsid[] UNUSED = "$Id: hparam.c,v 1.65 2005/09/14 08:57:00 lwa Exp $"; #define passwd system_passwd #include #include #if WITH_DB #include #endif #include #include "ascii-ctype.h" #include #include #include #include #undef passwd #include #include "hparam.h" #include "version.h" #define SEPS " \t\n" struct param home_param = {0,}; #if HAVE_GETPROGNAME # define HTAG_DEFAULT (char*)(getprogname()) #elif HAVE___PROGNAME extern char * __progname; # define HTAG_DEFAULT __progname #else # define HTAG_DEFAULT "" #endif static char *tag = NULL; int hparam_done=0; static void free_regexp_list(struct regexp_list *rl) { while(rl != NULL) { struct regexp_list *rln; regfree(rl->preg); free(rl->action); rln=rl->next; free(rl); rl=rln; } } static void free_words(char **words) { if (words) { free(*words); free(words); } } void home_clean(void) { if (hparam_done==0) return; free(home_param.query); #if WITH_MYSQL free_words(home_param.my_hosts); free(home_param.my_database); free(home_param.my_user); free(home_param.my_passwd); #endif #if WITH_PGSQL free_words(home_param.pg_hosts); free(home_param.pg_database); free(home_param.pg_user); free(home_param.pg_passwd); #endif #if WITH_LDAP free(home_param.ld_hosts); free(home_param.ld_dn); free(home_param.ld_passwd); free(home_param.ld_attrs); free_regexp_list(home_param.ld_base); home_param.ld_timeout = 0; home_param.ld_crypt = 0; home_param.ld_version = 0; #endif #if WITH_MYSQL || WITH_PGSQL || WITH_LDAP free(home_param.where_nam); free(home_param.where_uid); #endif #if WITH_PROXY free(home_param.proxy_socket); free_words(home_param.proxy_deny); #endif #if WITH_PAM free(home_param.pam_service); #endif free(home_param.pw_name); free(home_param.pw_passwd); free(home_param.pw_uid); free(home_param.pw_gid); free(home_param.pw_quota); free(home_param.pw_class); free(home_param.pw_gecos); free(home_param.pw_change); free(home_param.pw_dir); free(home_param.pw_shell); free(home_param.pw_expire); free(home_param.pw_alias); free(home_param.expire_fmt); free(home_param.errmsg); free_regexp_list(home_param.rewrite); free_regexp_list(home_param.fallback); free_regexp_list(home_param.hash); free_regexp_list(home_param.passwd_rew); free_words(home_param.pures); free_words(home_param.uid_calc); free_words(home_param.pwuid); #if WITH_DB free(home_param.cachefile); free_words(home_param.rewritedb); #endif home_param.driver = NULL; { struct param tmp = {0,}; memcpy(&home_param, &tmp, sizeof (struct param)); } hparam_done=0; } char *setpwtag(char *newtag) { if (newtag!=NULL) tag=newtag; return tag; } #if WITH_LDAP static char *expand_lookup(char *name, void *data) { return strdup(""); } static void expand_error(char *message, void *data) { home_retry("string expansion failed: ", message); } #endif struct regexp_list *compile_relist(char *line) { regex_t *preg=(regex_t*)malloc(sizeof(regex_t)); struct regexp_list *rl; int ret; char *xline=line; while(*xline && (!ASCII_ISBLANK(*xline) || (xline >line && xline[-1]=='\\'))) xline++; if (*xline) { *xline++=0; while(ASCII_ISBLANK(*xline)) xline++; } ret=regcomp(preg, line, REG_EXTENDED|REG_ICASE); if (ret) { char errbuf[LINEMAX]; regerror(ret, preg, errbuf, LINEMAX); free(preg); home_retry("bad regexp '%s': %s\n", line, errbuf); return NULL; } rl=(struct regexp_list *)malloc(sizeof(struct regexp_list)); if (rl==NULL) return hmalloc_error("regexp_list", NULL); rl->next=NULL; rl->preg=preg; if ((rl->action=strdup(xline))==NULL) { free(rl); return hmalloc_error("action", xline); } return rl; } #define FLAGS_LOCAL 0x0100 #define FLAGS_TYPE 0x00f0 /* type mask */ #define FLAGS_STRING 0x0010 #define FLAGS_INTEGER 0x0020 #define FLAGS_REGLIST 0x0030 #define FLAGS_WORDS 0x0040 #define FLAGS_BOOL 0x0080 struct cmdlist { const char *key; void *val; int flags; void *data; } ; static int keycmp(const char *a, const char *b) { while(1) { while(*a == '.' || *a == '_') a++; while(*b == '.' || *b == '_') b++; if (*a == *b) { if (*a == 0) return 0; a++; b++; } else { return 1; } } return 0; } struct param *home_init(char *file) { char *usercase = ""; char *homecase = ""; char *mode = "mysql"; #if WITH_MYSQL || WITH_PGSQL char *local_table="passwd"; #endif char *local_conditions=""; int intag=1; FILE *f; char line[LINEMAX]; #if WITH_LDAP || WITH_MYSQL || WITH_PGSQL int len = 0; #endif #if WITH_LDAP char **ldap_attrs = NULL; #endif int logversion=0; struct cmdlist off[] = { { "user", &home_param.pw_name, FLAGS_STRING, NULL}, { "uid", &home_param.pw_uid, FLAGS_STRING, NULL}, { "gid", &home_param.pw_gid, FLAGS_STRING, NULL}, { "home", &home_param.pw_dir, FLAGS_STRING, NULL}, { "passwd", &home_param.pw_passwd, FLAGS_STRING, NULL}, { "passwd_rew", &home_param.passwd_rew,FLAGS_REGLIST, &home_param.passwd_rew}, { "class", &home_param.pw_class, FLAGS_STRING, NULL}, { "alias", &home_param.pw_alias, FLAGS_STRING, NULL}, { "quota", &home_param.pw_quota, FLAGS_STRING, NULL}, { "shell", &home_param.pw_shell, FLAGS_STRING, NULL}, { "gecos", &home_param.pw_gecos, FLAGS_STRING, NULL}, { "expire", &home_param.pw_expire, FLAGS_STRING, NULL}, { "uid.calc", &home_param.uid_calc, FLAGS_WORDS, NULL}, { "getpwuid", &home_param.pwuid, FLAGS_WORDS, NULL}, { "expire_fmt", &home_param.expire_fmt, FLAGS_STRING, NULL}, { "quota_unit", &home_param.quota_unit, FLAGS_INTEGER, NULL}, { "conditions", &local_conditions, FLAGS_STRING|FLAGS_LOCAL, NULL}, #if WITH_MYSQL || WITH_PGSQL || WITH_LDAP { "where", &home_param.where_nam, FLAGS_STRING, NULL}, { "where.uid", &home_param.where_uid, FLAGS_STRING, NULL}, #endif { "rewrite", &home_param.rewrite, FLAGS_REGLIST, &home_param.rewrite}, { "fallback", &home_param.fallback, FLAGS_REGLIST, &home_param.fallback}, { "hash", &home_param.hash, FLAGS_REGLIST, &home_param.hash}, { "errmsg", &home_param.errmsg, FLAGS_STRING, NULL}, { "usercase", &usercase, FLAGS_STRING|FLAGS_LOCAL, NULL}, { "homecase", &homecase, FLAGS_STRING|FLAGS_LOCAL, NULL}, { "mode", &mode, FLAGS_STRING|FLAGS_LOCAL, NULL}, { "pures", &home_param.pures, FLAGS_WORDS, NULL}, { "retries", &home_param.retries, FLAGS_INTEGER, NULL}, { "retry.delay",&home_param.retry_delay, FLAGS_INTEGER, NULL}, #if WITH_DB { "cache_file", &home_param.cachefile, FLAGS_STRING, NULL}, { "cache_ttl", &home_param.cachettl, FLAGS_INTEGER, NULL}, { "cache_revive_ttl", &home_param.cacherevivettl, FLAGS_INTEGER, NULL}, { "cache_size", &home_param.cachesize, FLAGS_INTEGER, NULL}, { "cache_lockers", &home_param.cachelockers, FLAGS_INTEGER, NULL}, #endif { "backtime", &home_param.backtime, FLAGS_INTEGER, NULL}, #if WITH_MYSQL { "myhosts", &home_param.my_hosts, FLAGS_WORDS, NULL}, { "myport", &home_param.my_port, FLAGS_INTEGER, NULL}, { "mydatabase", &home_param.my_database, FLAGS_STRING, NULL}, { "myuser", &home_param.my_user, FLAGS_STRING, NULL}, { "mypasswd", &home_param.my_passwd, FLAGS_STRING, NULL}, { "mytable", &local_table, FLAGS_STRING|FLAGS_LOCAL, NULL}, { "myconnect_timeout", &home_param.my_connect_timeout, FLAGS_INTEGER, NULL}, #endif #if WITH_PGSQL { "pg_hosts", &home_param.pg_hosts, FLAGS_WORDS, NULL}, { "pg_database", &home_param.pg_database, FLAGS_STRING, NULL}, { "pg_user", &home_param.pg_user, FLAGS_STRING, NULL}, { "pg_passwd", &home_param.pg_passwd, FLAGS_STRING, NULL}, #endif #if WITH_MYSQL || WITH_PGSQL { "table", &local_table, FLAGS_STRING|FLAGS_LOCAL, NULL}, #endif { "sys_shadow", &home_param.sys_shadow, FLAGS_INTEGER, NULL}, { "sys_quota", &home_param.sys_quota, FLAGS_INTEGER, NULL}, #if WITH_PAM { "pam_service", &home_param.pam_service, FLAGS_STRING, NULL}, #endif #if WITH_LDAP { "ld_hosts", &home_param.ld_hosts, FLAGS_STRING, NULL}, { "ld_dn", &home_param.ld_dn, FLAGS_STRING, NULL}, { "ld_passwd", &home_param.ld_passwd, FLAGS_STRING, NULL}, { "ld_base", &home_param.ld_base, FLAGS_REGLIST, NULL}, { "ld_timeout", &home_param.ld_timeout, FLAGS_INTEGER, NULL}, { "ld_version", &home_param.ld_version, FLAGS_INTEGER, NULL}, { "ld_crypt", &home_param.ld_crypt, FLAGS_BOOL, NULL}, { "ld_extra_attributes", &ldap_attrs, FLAGS_WORDS, NULL}, #endif #if WITH_DB { "rewritedb", &home_param.rewritedb, FLAGS_WORDS, NULL}, #endif #if WITH_PROXY { "proxy_socket", &home_param.proxy_socket, FLAGS_STRING, NULL}, { "proxy.deny", &home_param.proxy_deny, FLAGS_WORDS, NULL}, #endif { "log.version", &logversion, FLAGS_BOOL, NULL}, { "log.stderr", &home_param.log_stderr, FLAGS_BOOL, NULL}, { "crypt_always_crypted", &home_param.crypt_always_crypted, FLAGS_BOOL, NULL}, { NULL,}, }, *off_off; if (tag==NULL) { setpwtag(HTAG_DEFAULT); } if (hparam_done) return &home_param; if (file==NULL) { file = DEFAULT_HOME_CONF; } if ((f=fopen(file, "r"))==NULL) { home_param.errmsg=""; home_retry("open %s: %s\n", file, strerror(errno)); return NULL; } /* default values */ home_param.quota_unit = 1; /* bytes */ /* lecture du fichier */ while (fgets(line, LINEMAX, f)!=NULL) { char *val; char *blank; char *xline=line; char *eline; /* skip starting spaces */ while(ASCII_ISBLANK(*xline)) xline++; eline=strchr(xline, '\n'); if (eline==NULL) { home_retry("line too long in config file %.20s", xline); return NULL; } if (eline==line || *xline=='#') continue; /* skip ending spaces */ eline--; while(eline>xline && ASCII_ISBLANK(*eline)) eline--; if (eline==line) continue; eline[1]=0; /* * syntaxe : [tag] la valeur 'tag' * : [tag*] quelque chose qui commence par 'tag' * : [tag1,tag2,tag3] un de ces trois là * */ if (*xline=='[') { intag=0; xline++; if (*eline==']') { char *etag; *eline--=0; do { etag = strchr(xline, ','); if (etag == NULL) etag = eline; else *etag--=0; if (*etag=='*') { if (strncmp(xline, tag, etag-xline)==0) intag=1; } else if (strcmp(xline, tag)==0) intag=1; xline = etag + 2; } while(etag < eline && intag == 0); } continue; } if (intag==0) continue; blank=strpbrk(xline, SEPS ); if (blank) { size_t nonblank=strspn(blank, SEPS); val=blank+nonblank; *blank=0; } else { val=""; } /* recherche du mot clef */ off_off=off; while(off_off->key!=NULL) { // fprintf(stderr, "cmp(%s, %s)\n", off_off->key, xline); if (keycmp(off_off->key, xline)==0) { // fprintf(stderr, "OK\n"); switch(off_off->flags & FLAGS_TYPE) { case FLAGS_REGLIST: { /* liste de regex */ struct regexp_list *rl=compile_relist(val); if (rl==NULL) return NULL; *(struct regexp_list **)(off_off->val)=rl; /* (struct regexp_list **)off_off->val=&(rl->next); hmm ?*/ off_off->val=&(rl->next); } break; case FLAGS_STRING: /* variable chaine */ if ((*(char **)(off_off->val)=strdup(val))==NULL) return hmalloc_error("home_init", val); if (off_off->data) free(off_off->data); off_off->data=*(char **)(off_off->val); break; case FLAGS_INTEGER: *(int*)off_off->val=strtol(val, NULL, 0); break; case FLAGS_BOOL: *(int*)off_off->val=1; switch(*val) { case 'o': case 'O': if (val[1]!='f' && val[1]!='F') break; case '0': case 'n': case 'f': *(int*)off_off->val=0; } case FLAGS_WORDS: { /* table de mots */ int wc=0; /* 2 entrées pour commencer */ char **wt=(char**)malloc(sizeof(char**)*2); if ((wt[0]=strdup(val))==NULL) { hmalloc_error("home_init", val); return NULL; } val=wt[0]; if (off_off->data) { if (*(char**)off_off->data) free(*(char**)off_off->data); free(off_off->data); } while(*val) { if (ASCII_ISBLANK(*val)) { while(ASCII_ISBLANK(*val)) *val++=0; if (*val==0) break; wt[++wc]=val; wt=(char**)realloc(wt,sizeof(char**)*(wc+2)); } val++; } wt[wc+1]=NULL; /* (char **)(off_off->data)=wt; hmm? */ off_off->data=wt; *(char ***)(off_off->val)=wt; } break; } } if (off_off->key==NULL) { /* home_retry("bad keyword '%s'\n", xline); return NULL; */ } off_off++; } } fclose(f); if (logversion) { home_error("libhome: tag=%s, " LIBHOME_VERSION, tag); #ifdef DB_VERSION_STRING if (home_param.cachefile) home_error("libhome: %s", DB_VERSION_STRING); #endif } /* prepare la requete SQL */ switch(*usercase) { case 'l': case 'L': home_param.usercase = HCASE_LOWER; break; case 'u': case 'U': home_param.usercase = HCASE_UPPER; break; default: home_param.usercase = HCASE_NONE; break; } switch(*mode) { case 'm': case 'M': home_param.mode = HOME_MODE_MYSQL; break; case 'l': case 'L': home_param.mode = HOME_MODE_LDAP; break; case 's': case 'S': home_param.mode = HOME_MODE_SYSTEM; break; case 'p': case 'P': switch(mode[1]) { case 'g': case 'G': home_param.mode = HOME_MODE_PGSQL; break; case 'a': case 'A': home_param.mode = HOME_MODE_PAM; break; case 'r': case 'R': home_param.mode = HOME_MODE_PROXY; break; } break; default: home_param.mode = HOME_MODE_DEFAULT; break; } if (homecase && *homecase) { if (strcasecmp(homecase, "trylower") == 0) home_param.homecase = HCASE_TRYLOWER; else if (strcasecmp(homecase, "tryupper") == 0) home_param.homecase = HCASE_TRYUPPER; else if (strcasecmp(homecase, "lower")==0) home_param.homecase = HCASE_LOWER; else if (strcasecmp(homecase, "upper")==0) home_param.homecase = HCASE_UPPER; else if (strcasecmp(homecase, "trynull")==0) home_param.homecase = HCASE_TRYNULL; } else { home_param.homecase = HCASE_NONE; } switch(home_param.mode) { #if WITH_MYSQL || WITH_PGSQL case HOME_MODE_PGSQL: case HOME_MODE_MYSQL: { char *val_name = home_param.pw_name ? home_param.pw_name : "user"; char *val_dir = home_param.pw_dir ? home_param.pw_dir : "NULL"; char *val_passwd = home_param.pw_passwd ? home_param.pw_passwd : "passwd"; char *val_uid = home_param.pw_uid ? home_param.pw_uid : "-1"; char *val_gid = home_param.pw_gid ? home_param.pw_gid : "-1"; char *val_class = home_param.pw_class ? home_param.pw_class : "''"; char *val_alias = home_param.pw_alias ? home_param.pw_alias : "NULL"; char *val_quota = home_param.pw_quota ? home_param.pw_quota : "0"; char *val_expire = home_param.pw_expire ? home_param.pw_expire : "''"; char *val_shell = home_param.pw_shell ? home_param.pw_shell : "''"; char *val_gecos = home_param.pw_gecos ? home_param.pw_gecos : "''"; if (!home_param.where_uid) home_param.where_uid = strdup(val_uid); if (!home_param.where_nam) home_param.where_nam = strdup(val_name); #if WITH_PGSQL if (home_param.mode == HOME_MODE_PGSQL) { extern struct home_driver hpgsql_driver; home_param.driver = &hpgsql_driver; } #endif #if WITH_MYSQL if (home_param.mode == HOME_MODE_MYSQL) { extern struct home_driver hmysql_driver; home_param.driver = &hmysql_driver; } #endif if (local_conditions && *local_conditions) { len=snprintf(line, LINEMAX, "select %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s from %s where %s%n and %%s = '%%s'", val_name, val_passwd, val_uid, val_gid, val_class, val_dir, val_quota, val_alias, val_shell, val_gecos, val_expire, local_table, local_conditions, &(home_param.sql_partial) ); } else { len=snprintf(line, LINEMAX, "select %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s from %s%n where %%s = '%%s'", val_name, val_passwd, val_uid, val_gid, val_class, val_dir, val_quota, val_alias, val_shell, val_gecos, val_expire, local_table, &(home_param.sql_partial) ); } } break; #endif #if WITH_LDAP case HOME_MODE_LDAP: { extern struct home_driver hldap_driver; int nattr = 0; char **attr = ldap_attrs; if (attr) while(*attr++) nattr++; if (!home_param.where_uid) home_param.where_uid = strdup(home_param.pw_uid); if (!home_param.where_nam) home_param.where_nam = strdup(home_param.pw_name); home_param.driver = &hldap_driver; if (local_conditions && *local_conditions) { len=snprintf(line, LINEMAX, "(&(%%s=%%s)%s)", local_conditions); } else { len=snprintf(line, LINEMAX, "(%%s=%%s)"); } #define add_attr(x) \ if (x && ASCII_ISALPHA(*x)) { \ ldap_attrs=realloc(ldap_attrs, sizeof(char *)*(nattr+2)); \ ldap_attrs[nattr++]=x; \ } else if (x && *x == '=') { \ char *message = NULL; \ char *ret = hexpand_string(x+1, expand_lookup, expand_error, message); \ if (ret == NULL) \ return NULL; \ } \ add_attr(home_param.pw_name); add_attr(home_param.pw_passwd); add_attr(home_param.pw_uid); add_attr(home_param.pw_gid); add_attr(home_param.pw_quota); add_attr(home_param.pw_class); add_attr(home_param.pw_change); add_attr(home_param.pw_gecos); add_attr(home_param.pw_dir); add_attr(home_param.pw_shell); add_attr(home_param.pw_expire); add_attr(home_param.pw_alias); if (ldap_attrs) ldap_attrs[nattr] = NULL; home_param.ld_attrs = ldap_attrs; } break; #endif #if WITH_PAM case HOME_MODE_PAM: { extern struct home_driver hpam_driver; home_param.driver = &hpam_driver; break; } #endif case HOME_MODE_SYSTEM: { extern struct home_driver hsystem_driver; home_param.driver = &hsystem_driver; home_param.crypt_always_crypted = 1; break; } #if WITH_PROXY case HOME_MODE_PROXY: { extern struct home_driver hproxy_driver; home_param.driver = &hproxy_driver; free_words(home_param.uid_calc); home_param.uid_calc = NULL; #ifdef DB_VERSION_STRING home_param.cachefile = NULL; /* disable the cache for proxy */ #endif break; } #endif } /* end switch */ #if WITH_LDAP || WITH_MYSQL || WITH_PGSQL if (len>=LINEMAX) { home_retry("param query too long %100.100s...", line); return NULL; } if ((home_param.query=strdup(line))==NULL) return hmalloc_error("home_init query", line); #endif #if WITH_PAM if (home_param.pam_service == NULL || *home_param.pam_service == 0) { home_param.pam_service = "other"; } #endif off_off=off; while(off_off->key!=NULL) { /* libère la mémoire allouée */ if (off_off->data && off_off->flags & FLAGS_LOCAL) { switch (off_off->flags & ~FLAGS_LOCAL) { case FLAGS_WORDS: free_words(off_off->data); break; case FLAGS_STRING: free(off_off->data); break; } off_off->data=NULL; } off_off++; } hparam_done=1; return &home_param; }