authldaplib.c
上传用户:s81996212
上传日期:2007-01-04
资源大小:722k
文件大小:14k
- /*
- * authldap.c -
- *
- * courier-imap -
- *
- * Copyright 1999 Luc Saillard <luc.saillard@alcove.fr>.
- *
- * This module use a server LDAP to authenticate user.
- * See the README.ldap
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
- /*
- Modified 01/21/2000 James Golovich <james@wwnet.net>
- 1. If LDAP_AUTHBIND is set in the config file, then the ldap server will
- handle passwords via authenticated binds, instead of checking these
- internally.
- 2. Changed paramaters for authldap_get to include pass.
- 3. Added int authbind and int authbind_ok to struct ldap_info to
- handle authenticated binds.
- */
- /*
- Modified 12/31/99 Sam Varshavchik:
- 1. read_env reads from a configuration file, instead of the environment
- 2. read_config appropriately modified.
- 3. If 'user' contains the @ character, domain from config is NOT appended.
- 4. added 'homeDir' attribute. Use 'homeDir' instead of mailDir, and put
- mailDir into MAILDIR=
- 5. read_config renamed to authldap_read_config
- 6. get_user_info renamed to authldap_get
- 7. Added authldap_free_config, to clean up all the allocated memory
- (required for preauthldap).
- 8. Output LDAP attributes are defined in the configuration file as well.
- 9. Allow both plaintext and crypted passwords to be read from LDAP.
- 10. Added GLOB_UID GLOB_GID, as well as UID and GID params.
- 2/19/2000 Sam.
- Rewrite to allow this code to be used in a long-running authentication daemon
- (for Courier). authldap_get renamed to authldapcommon, will initialize and
- maintain a persistent connection. Password checking moved entirely to
- authldap.c. authldapclose() will unbind and close the connection.
- connection gets closed and reopened automatically after a protocol error.
- error return from authldapcommon will indicate whether this is a transient,
- or a permanent failure.
- authldap_free_config removed - no longer required.
- */
- #if HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <string.h>
- #include <errno.h>
- #include <pwd.h>
- #include <grp.h>
- #if HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #if HAVE_LBER_H
- #include <lber.h>
- #endif
- #if HAVE_LDAP_H
- #include <ldap.h>
- #endif
- #if HAVE_SYS_TIME_H
- #include <sys/time.h>
- #endif
- #if HAVE_SYS_STAT_H
- #include <sys/stat.h>
- #endif
- #if HAVE_SYSLOG_H
- #include <syslog.h>
- #else
- #define syslog(a,b)
- #endif
- #include "authldap.h"
- #include "auth.h"
- #include "authldaprc.h"
- #define DEBUG_LDAP 0
- struct ldap_info
- {
- const char *hostname;
- int port;
- const char *binddn;
- const char *bindpw;
- const char *basedn;
- const char *mail;
- const char *domain;
- uid_t uid;
- gid_t gid;
- int timeout;
- int authbind;
- int authbind_ok;
- };
- static int read_env(const char *env, const char **copy,
- const char *err, int needit, const char *value_default);
- static void copy_value(LDAP *ld, LDAPMessage *entry, const char *attribut,
- char **copy);
- /*
- * Function: read_env
- * Copy the environnement $env and copy to $copy if not null
- * if needit is false, and env doesn't exist, copy $value_default to $copy
- * INPUT:
- * $env: pointer to the environnement name
- * $copy: where the value go
- * $err: print a nice message when $env not_found and $needit is true
- * $needit: if $needit is true and $value not found, return a error
- * $value_default: the default value when $needit is false and $env doesn't exists
- * OUTPUT:
- * boolean
- */
- static int read_env(const char *env, const char **copy,
- const char *err, int needit, const char *value_default)
- {
- static char *ldapauth=0;
- static size_t ldapauth_size;
- size_t i;
- char *p;
- int l=strlen(env);
- if (!ldapauth)
- {
- FILE *f=fopen(AUTHLDAPRC, "r");
- struct stat buf;
- if (!f) return (0);
- if (fstat(fileno(f), &buf) ||
- (ldapauth=malloc(buf.st_size+2)) == 0)
- {
- fclose(f);
- return (0);
- }
- if (fread(ldapauth, buf.st_size, 1, f) != 1)
- {
- free(ldapauth);
- ldapauth=0;
- fclose(f);
- return (0);
- }
- ldapauth[ldapauth_size=buf.st_size]=0;
- for (i=0; i<ldapauth_size; i++)
- if (ldapauth[i] == 'n')
- ldapauth[i]=0;
- }
- for (i=0; i<ldapauth_size; )
- {
- p=ldapauth+i;
- if (memcmp(p, env, l) == 0 &&
- isspace((int)(unsigned char)p[l]))
- {
- p += l;
- while (*p && *p != 'n' &&
- isspace((int)(unsigned char)*p))
- ++p;
- break;
- }
- while (i < ldapauth_size)
- if (ldapauth[i++] == 0) break;
- }
- if (i < ldapauth_size)
- {
- *copy= p;
- return (1);
- }
- if (needit)
- {
- fprintf(stderr, "ERR: %sn",err);
- fflush(stderr);
- return 0;
- }
- *copy=0;
- if (value_default)
- *copy=value_default;
- return 1;
- }
- /*
- * Function: authldap_read_config
- * Read Configuration from the environnement table
- * INPUT:
- * $ldap: a structure where we place information
- * OUTPUT:
- * boolean
- */
- static int authldap_read_config(struct ldap_info *ldap)
- {
- struct passwd *pwent;
- struct group *grent;
- const char *p;
- memset(ldap,0,sizeof(struct ldap_info));
-
- if (!read_env("LDAP_SERVER",&ldap->hostname,"You need to specify a ldap server in config file",1,NULL))
- return 0;
- if (!read_env("LDAP_AUTHBIND", &p, "", 0, ""))
- return (0);
- if (p)
- sscanf(p,"%d",&ldap->authbind);
- ldap->port=LDAP_PORT;
- if (!read_env("LDAP_PORT", &p, "", 0, ""))
- return (0);
- if (p)
- sscanf(p,"%d",&ldap->port);
- if (!read_env("LDAP_BASEDN",&ldap->basedn,"You need to specify a basedn in config file",1,NULL))
- return 0;
- if (!read_env("LDAP_BINDDN",&ldap->binddn,"You need to specify a BINDDN in config file",0,NULL))
- return 0;
- if (!read_env("LDAP_BINDPW",&ldap->bindpw,"You need to specify a password for the BINDDN in config file",0,NULL))
- return 0;
- if (!read_env("LDAP_MAIL",&ldap->mail,"You need to specify a attribute for mail in config file",0,"mail"))
- return 0;
- if (!read_env("LDAP_DOMAIN",&ldap->domain,"You need to specify a domain for mail in config file",0,"*"))
- return 0;
- p=0;
- ldap->uid=0;
- if (!read_env("LDAP_GLOB_UID", &p, "", 0, ""))
- return (0);
- if (p && *p)
- {
- unsigned long n;
- if (sscanf(p, "%lu", &n) == 1)
- ldap->uid=(uid_t)n;
- else
- {
- pwent=getpwnam(p);
- if (!pwent)
- {
- syslog(LOG_DAEMON|LOG_CRIT,
- "authldap: INVALID LDAP_GLOB_UIDn");
- return (0);
- }
- ldap->uid=pwent->pw_uid;
- }
- }
- ldap->gid=0;
- p=0;
- if (!read_env("LDAP_GLOB_GID", &p, "", 0, ""))
- return (0);
- if (p && *p)
- {
- unsigned long n;
- if (sscanf(p, "%lu", &n) == 1)
- ldap->gid=(gid_t)n;
- else
- {
- grent=getgrnam(p);
- if (!grent)
- {
- syslog(LOG_DAEMON|LOG_CRIT,
- "authldap: INVALID LDAP_GLOB_GIDn");
- return (0);
- }
- ldap->gid=grent->gr_gid;
- }
- }
- ldap->timeout=5;
- p=0;
- if (!read_env("LDAP_TIMEOUT", &p, "", 0, ""))
- return (0);
- if (p)
- {
- sscanf(p,"%d",&ldap->timeout);
- }
- return 1;
- }
- /*
- * Function: copy_value
- * Copy value from a LDAP attribute to $copy
- * INPUT:
- * $ld: the connection with the LDAP server
- * $entry: the entry who contains attributes
- * $attribut: this attribut
- * $copy: where data can go
- * OUTPUT:
- * void
- */
- static void copy_value(LDAP *ld, LDAPMessage *entry, const char *attribut,
- char **copy)
- {
- char ** values;
- values=ldap_get_values(ld,entry, (char *)attribut);
- if (values==NULL)
- {
- if (ld->ld_errno != LDAP_DECODING_ERROR)
- /* We didn't ask for this attribute */
- ldap_perror(ld,"ldap_get_values");
- *copy=NULL;
- return;
- }
- /* We accept only attribute with one value */
- else if (ldap_count_values(values)!=1)
- {
- *copy=NULL;
- }
- else
- {
- *copy=strdup(values[0]);
- }
- ldap_value_free(values);
- }
- static struct ldap_info my_ldap;
- static LDAP *my_ldap_fp=0;
- void authldapclose()
- {
- if (my_ldap_fp)
- {
- ldap_unbind(my_ldap_fp);
- my_ldap_fp=0;
- }
- }
- static int ldaperror(int rc)
- {
- if (rc && !NAME_ERROR(rc))
- /* If there was a protocol error, close the connection */
- authldapclose();
- return (rc);
- }
- static LDAP *ldapconnect()
- {
- LDAP *p;
- #if DEBUG_LDAP
- printf("Hostname: %s:%dn",my_ldap.hostname,my_ldap.port);
- printf("UID: %dn",my_ldap.uid);
- printf("GID: %dn",my_ldap.gid);
- #endif
- p=ldap_open((char *)my_ldap.hostname,my_ldap.port);
- if (p==NULL)
- {
- #if HAVE_SYSLOG_H
- syslog(LOG_DAEMON|LOG_CRIT,
- "Cannot connect to LDAP server (%s:%d): %s",
- my_ldap.hostname, my_ldap.port, strerror(errno));
- #else
- fprintf(stderr,
- "Cannot connect to LDAP server (%s:%d): %s",
- my_ldap.hostname, my_ldap.port, strerror(errno));
- #endif
- }
- return (p);
- }
- static int ldapopen()
- {
- if (my_ldap_fp) return (0);
- if (authldap_read_config(&my_ldap) == 0)
- return (1);
- my_ldap_fp=ldapconnect();
- if (!my_ldap_fp)
- {
- return (1);
- }
- /* Bind to server */
- #if DEBUG_LDAP
- printf("BindDn: %snBindPw: %sn",my_ldap.binddn,my_ldap.bindpw);
- #endif
- if (ldaperror(ldap_simple_bind_s(my_ldap_fp,
- (char *)my_ldap.binddn,
- (char *)my_ldap.bindpw)) != LDAP_SUCCESS)
- {
- const char *s=ldap_err2string(my_ldap_fp->ld_errno);
- #if HAVE_SYSLOG_H
- syslog(LOG_DAEMON|LOG_CRIT, "ldap_simple_bind_s failed: %s", s);
- #endif
- authldapclose();
- return (-1);
- }
- return (0);
- }
- /*
- * Function: authldapcommon
- * Get information from the LDAP server ($ldap) for this $user
- * INPUT:
- * $user: the login name
- * $pass: the login password (NULL if we don't want to check the pw)
- * callback - callback function with filled in authentication info
- * arg - extra argument for the callback function.
- * OUTPUT:
- * < 0 - authentication failure
- * > 0 - temporary failure
- * else return code from the callback function.
- */
- int authldapcommon(const char *user, const char *pass,
- int (*callback)(struct authinfo *, void *),
- void *arg)
- {
- const char *attributes[10], *ldap_attributes[10];
-
- struct timeval timeout;
- LDAPMessage *result;
- LDAPMessage *entry;
- char *filter, *dn;
- int i, j;
- struct authinfo auth;
- char *homeDir=0;
- char *mailDir=0;
- char *userPassword=0;
- char *cryptPassword=0;
- char *cn=0;
- uid_t au;
- gid_t ag;
- int rc;
- char *quota=0;
- memset(&auth, 0, sizeof(auth));
- if (ldapopen()) return (1);
- if ((filter=malloc(strlen(my_ldap.mail)+strlen(user)+
- (my_ldap.domain ? strlen(my_ldap.domain):0)+
- sizeof ("(=@)"))) == 0)
- {
- perror("malloc");
- return 1;
- }
- strcat(strcat(strcat(strcpy(filter, "("), my_ldap.mail), "="), user);
- if ( strchr(user, '@') == 0)
- strcat(strcat(filter, "@"), my_ldap.domain);
- strcat(filter, ")");
- timeout.tv_sec=my_ldap.timeout;
- timeout.tv_usec=0;
- read_env("LDAP_HOMEDIR", &attributes[0], "", 0, "homeDir");
- read_env("LDAP_MAILDIR", &attributes[1], "", 0, 0);
- read_env("LDAP_FULLNAME", &attributes[2], "", 0, "cn");
- read_env("LDAP_CLEARPW", &attributes[3], "", 0, 0);
- read_env("LDAP_CRYPTPW", &attributes[4], "", 0, 0);
- read_env("LDAP_UID", &attributes[5], "", 0, 0);
- read_env("LDAP_GID", &attributes[6], "", 0, 0);
- attributes[7]=my_ldap.mail;
- read_env("LDAP_MAILDIRQUOTA", &attributes[8], "", 0, 0);
- j=0;
- for (i=0; i<9; i++)
- {
- if (attributes[i])
- ldap_attributes[j++]=attributes[i];
- }
- ldap_attributes[j]=0;
- if (ldaperror(ldap_search_st(my_ldap_fp, (char *)my_ldap.basedn,LDAP_SCOPE_SUBTREE,
- filter, (char **)ldap_attributes, 0, &timeout, &result)
- != LDAP_SUCCESS))
- {
- free(filter);
- if (my_ldap_fp) return (1);
- return (-1);
- }
- free(filter);
- /* If we are more than one result, reject */
- if (ldap_count_entries(my_ldap_fp,result)!=1)
- {
- ldap_msgfree(result);
- return -1;
- }
- #if DEBUG_LDAP
- printf("Nombre de r閟ulat: %dn",ldap_count_entries(ld,result));
- #endif
- dn = ldap_get_dn(my_ldap_fp, result);
- #if DEBUG_LDAP
- printf("DN: %sn",dn);
- #endif
- if (dn == NULL)
- {
- ldap_perror(my_ldap_fp, "ldap_get_dn");
- return -1;
- }
- /* Get the pointer on this result */
- entry=ldap_first_entry(my_ldap_fp,result);
- if (entry==NULL)
- {
- ldap_perror(my_ldap_fp,"ldap_first_entry");
- free(dn);
- return -1;
- }
- /* Copy the directory and the password into struct */
- copy_value(my_ldap_fp,entry,attributes[0],&homeDir);
- if (attributes[1])
- copy_value(my_ldap_fp,entry,attributes[1],&mailDir);
- copy_value(my_ldap_fp,entry,attributes[2],&cn);
- if (attributes[3])
- copy_value(my_ldap_fp,entry,attributes[3],&userPassword);
- if (attributes[4])
- copy_value(my_ldap_fp,entry,attributes[4],&cryptPassword);
- au=my_ldap.uid;
- ag=my_ldap.gid;
- if (attributes[5])
- {
- char *p=0;
- unsigned long n;
- copy_value(my_ldap_fp, entry, attributes[5], &p);
- if (!p) return (0);
- if (sscanf(p, "%lu", &n) > 0)
- au= (uid_t)n;
- free(p);
- }
- if (attributes[6])
- {
- char *p=0;
- unsigned long n;
- copy_value(my_ldap_fp, entry, attributes[6], &p);
- if (!p) return (0);
- if (sscanf(p, "%lu", &n) > 0)
- ag= (gid_t)n;
- free(p);
- }
- if (attributes[8])
- copy_value(my_ldap_fp,entry,attributes[8],"a);
- auth.sysusername=user;
- auth.sysuserid= &au;
- auth.sysgroupid= ag;
- auth.homedir=homeDir;
- auth.address=user;
- auth.fullname=cn;
- auth.maildir=mailDir;
- auth.clearpasswd=userPassword;
- auth.passwd=cryptPassword;
- auth.quota=quota;
- if (auth.sysusername == 0)
- auth.sysusername=auth.address="";
- if (homeDir == 0)
- auth.homedir="";
- rc=0;
- if (pass)
- {
- if (my_ldap.authbind)
- {
- LDAP *bindp=ldapconnect();
- if (!bindp)
- rc=1;
- else
- {
- if (ldaperror(
- ldap_simple_bind_s(bindp, dn, (char *)pass)
- != LDAP_SUCCESS))
- {
- rc= my_ldap_fp ? 1:-1;
- }
- ldap_unbind(bindp);
- }
- }
- else
- {
- if (auth.clearpasswd)
- {
- if (strcmp(pass,auth.clearpasswd))
- rc= -1;
- }
- else
- {
- const char *p=auth.passwd;
- if (p && strncmp(p, "{crypt}", 7) == 0)
- p += 7; /* For authcheckpassword */
- if (!p || authcheckpassword(pass, p))
- rc= -1;
- }
- }
- }
- free (dn);
- if (rc == 0)
- rc= (*callback)(&auth, arg);
- ldap_msgfree(result);
- if (homeDir) free(homeDir);
- if (mailDir) free(mailDir);
- if (userPassword) free(userPassword);
- if (cryptPassword) free(cryptPassword);
- if (cn) free(cn);
- if (quota) free(quota);
- return (rc);
- }