/* 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: hldap.c,v 1.30 2005/06/23 13:02:58 lwa Exp $"; #define passwd system_passwd #include /* for openldap 1.x */ #include #include #include #include #include #include #if HAVE_CRYPT_H #include #endif #undef passwd #include "hparam.h" static LDAP *ld=NULL; extern struct param home_param; extern int home_stayopen; #ifndef LDAP_SECURITY_ERROR #define LDAP_SECURITY_ERROR(n) ((n)>= 0x30 && (n) <= 0x32) #endif #ifndef LDAP_SERVICE_ERROR #define LDAP_SERVICE_ERROR(n) ((n)>= 0x33 && (n) <= 0x36) #endif #ifndef LDAP_API_ERROR #define LDAP_API_ERROR(n) ((n)>= 0x51 && (n) <= 0x61) #endif static void *hldap_error(char *context, int rc) { home_retry("LDAP %s error 0x%x: %s", context, rc, ldap_err2string(rc)); return NULL; } static void hldap_clean(void) { if (ld) { ldap_unbind(ld); ld=NULL; } } static int hldap_open(void) { int rc; if (ld) return 0; if ((ld=ldap_init(home_param.ld_hosts, LDAP_PORT))==NULL) { home_retry("LDAP open error: %s", strerror(errno)); return -1; } # ifdef LDAP_OPT_PROTOCOL_VERSION if (home_param.ld_version != 0) { int version = home_param.ld_version; if ((rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version)) != LDAP_SUCCESS) { hldap_error("ldap_set_option", rc); return -1; } } # endif #if LDAP_API_VERSION >= 2000 ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON); #ifdef LDAP_OPT_NETWORK_TIMEOUT if (home_param.ld_timeout) { struct timeval tv; tv.tv_sec = home_param.ld_timeout; tv.tv_usec = 0; ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv); } #endif #if LDAP_OPT_TIMEOUT if (home_param.ld_timeout) { struct timeval tv; tv.tv_sec = home_param.ld_timeout; tv.tv_usec = 0; ldap_set_option(ld, LDAP_OPT_TIMEOUT, &tv); } #endif #else ld->ld_options |= LDAP_OPT_RESTART; #endif /* ld->ld_options|=LDAP_OPT_RESTART; */ if ((rc=ldap_simple_bind_s(ld, home_param.ld_dn, home_param.ld_passwd)) !=LDAP_SUCCESS) { hldap_error("bind", rc); ldap_unbind(ld); ld=NULL; return -1; } return 0; } static LDAPMessage *hldap_query(char *rentry) { LDAPMessage *res=0; char fquery[LINEMAX]; char xentry[MAXUSERLEN*3+1]; char *eentry=xentry; char *s; int rc; char *base; struct timeval tv, *tv_p; if (hldap_open()==-1) return NULL; base=hrewrite(home_param.ld_base, rentry, HREW_NONULL|HREW_FIRST); if (base==NULL) return NULL; if (IS_UID(rentry)) { int fquery_len; fquery_len=snprintf(fquery, LINEMAX, home_param.query, home_param.where_uid, rentry); if (fquery_len >= LINEMAX) { home_error("filter too long for UID %.20s...", rentry); free(base); return NULL; } } else { int fquery_len; s=rentry; while(*s) { switch(*s) { case '*': *eentry++='\\'; *eentry++='2'; *eentry++='a'; break; case '(': *eentry++='\\'; *eentry++='2'; *eentry++='8'; break; case ')': *eentry++='\\'; *eentry++='2'; *eentry++='0'; break; case '\\': *eentry++='\\'; *eentry++='5'; *eentry++='c'; break; case '\0': *eentry++=0; break; default: *eentry++=*s; break; } s++; } *eentry=0; fquery_len=snprintf(fquery, LINEMAX, home_param.query, home_param.where_nam, xentry); if (fquery_len >= LINEMAX) { home_error("filter too long for user %.20s...", rentry); free(base); return NULL; } } if (home_param.ld_timeout > 0) { tv.tv_sec = home_param.ld_timeout; tv.tv_usec = 0; tv_p = &tv; } else { tv_p = NULL; } rc=ldap_search_st(ld, base, LDAP_SCOPE_SUBTREE, fquery, home_param.ld_attrs, 0, tv_p, &res); free(base); switch(rc) { case LDAP_SUCCESS: return res; case LDAP_NO_SUCH_OBJECT: break; default: home_retry("LDAP search error 0x%x: %s", rc, ldap_err2string(rc)); if (LDAP_SECURITY_ERROR(rc) || LDAP_SERVICE_ERROR(rc) || (LDAP_API_ERROR(rc) && rc != LDAP_FILTER_ERROR)) hldap_clean(); } return NULL; } static char *expand_lookup(char *name, void *data) { LDAPMessage *entry = data; char **val = ldap_get_values(ld, entry, name); char *ret = NULL; if (val == NULL) { return strdup(""); } ret = strdup(*val ? *val : ""); ldap_value_free(val); return ret; } static void expand_error(char *message, void *data) { } static char *hldap_get_value(LDAPMessage *entry, char *attr) { char *ret = NULL; /* avoid unitialized value warning */ if (attr==NULL) return strdup(""); if (*attr== '=') { ret = hexpand_string(attr + 1, expand_lookup, expand_error, entry); } else if (*attr=='\'' || *attr=='"') { int len; len=strlen(attr); if (attr[0]==attr[len]); /* TODO: something smart here */ ret=strdup(attr+1); ret[len-2]=0; } else { char **val; val=ldap_get_values(ld, entry, attr); if (val==NULL) { return strdup(""); } if (*val!=NULL) { ret=strdup(*val); } ldap_value_free(val); } return ret; } static struct passwd *hldap_store(LDAPMessage *res, char **alias) { LDAPMessage *entry; struct passwd *pwd; char *s; entry=ldap_first_entry(ld, res); if (entry==NULL) { ldap_msgfree(res); return NULL; } if (alias) { s=hldap_get_value(entry, home_param.pw_alias); if (*alias==NULL && s && *s) { *alias=s; ldap_msgfree(res); return NULL; } else { free(s); *alias=NULL; } } s=hldap_get_value(entry, home_param.pw_name); if (s==NULL || *s==0) { free(s); ldap_msgfree(res); return NULL; } pwd = home_getpwd(); pwd->pw_name=s; pwd->pw_passwd=hldap_get_value(entry, home_param.pw_passwd); if (home_param.ld_crypt && pwd->pw_passwd && strncasecmp(pwd->pw_passwd, "{crypt}",sizeof("{crypt}")-1)!=0) { char *cp=crypt(pwd->pw_passwd, "xy"); free(pwd->pw_passwd); pwd->pw_passwd=malloc(sizeof("{crypt}")+strlen(cp)); if (pwd->pw_passwd!=NULL) { strcat(strcpy(pwd->pw_passwd, "{crypt}"), cp); } else { hmalloc_error("hldap_store", "ld_crypt"); } } s=hldap_get_value(entry, home_param.pw_uid); pwd->pw_uid=s&&*s ? (uid_t)home_calc(strtoul(s, NULL, 10), home_param.uid_calc): (uid_t)-1; free(s); s=hldap_get_value(entry, home_param.pw_gid); pwd->pw_gid=s&&*s ? (gid_t)strtoul(s, NULL, 10) : (gid_t)-1; free(s); #ifdef HAS_PW_CLASS pwd->pw_class=hldap_get_value(entry, home_param.pw_class); #endif pwd->pw_gecos=hldap_get_value(entry, home_param.pw_gecos); s=hldap_get_value(entry, home_param.pw_dir); pwd->pw_dir=hexpand_home(pwd->pw_name, s); free(s); pwd->pw_shell=hldap_get_value(entry, home_param.pw_shell); #ifdef HAS_PW_CHANGE pwd->pw_change=0; #endif #ifdef HAS_PW_EXPIRE s=hldap_get_value(entry, home_param.pw_expire); pwd->pw_expire=home_expire(s); free(s); #endif #ifdef HAS_PW_FIELDS /* internal FreeBSD */ #endif #ifdef HAS_PW_QUOTA s=hldap_get_value(entry, home_param.pw_quota); pwd->pw_quota=s&&*s ? (int)strtol(s, NULL, 10) : 0; pwd->pw_quota *= home_param.quota_unit; free(s); #endif ldap_msgfree(res); return pwd; } struct home_driver hldap_driver = { (home_query_t) hldap_query, (home_store_t) hldap_store, (home_clean_t) hldap_clean, };