authldaplib.c
上传用户:s81996212
上传日期:2007-01-04
资源大小:722k
文件大小:14k
源码类别:

WEB邮件程序

开发平台:

C/C++

  1. /*
  2.  * authldap.c - 
  3.  *
  4.  * courier-imap - 
  5.  * 
  6.  * Copyright 1999 Luc Saillard <luc.saillard@alcove.fr>.
  7.  *
  8.  * This module use a server LDAP to authenticate user.
  9.  * See the README.ldap
  10.  *
  11.  * This program is free software; you can redistribute it and/or
  12.  * modify it under the terms of the GNU General Public License
  13.  * as published by the Free Software Foundation; either version 2
  14.  * of the License, or (at your option) any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  19.  *
  20.  * See the GNU General Public License for more details.
  21.  * 
  22.  * You should have received a copy of the GNU General Public License
  23.  * along with this program; see the file COPYING. If not, write to
  24.  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
  25.  * Boston, MA  02111-1307, USA.
  26.  */
  27. /*
  28.    Modified 01/21/2000 James Golovich <james@wwnet.net>
  29. 1. If LDAP_AUTHBIND is set in the config file, then the ldap server will
  30. handle passwords via authenticated binds, instead of checking these
  31. internally.
  32. 2. Changed paramaters for authldap_get to include pass.
  33. 3. Added int authbind and int authbind_ok to struct ldap_info to
  34. handle authenticated binds.
  35. */
  36. /*
  37.    Modified 12/31/99 Sam Varshavchik:
  38. 1. read_env reads from a configuration file, instead of the environment
  39. 2. read_config appropriately modified.
  40. 3. If 'user' contains the @ character, domain from config is NOT appended.
  41. 4. added 'homeDir' attribute.  Use 'homeDir' instead of mailDir, and put
  42.    mailDir into MAILDIR=
  43. 5. read_config renamed to authldap_read_config
  44. 6. get_user_info renamed to authldap_get
  45. 7. Added authldap_free_config, to clean up all the allocated memory
  46.    (required for preauthldap).
  47. 8. Output LDAP attributes are defined in the configuration file as well.
  48. 9. Allow both plaintext and crypted passwords to be read from LDAP.
  49. 10. Added GLOB_UID GLOB_GID, as well as UID and GID params.
  50. 2/19/2000 Sam.
  51. Rewrite to allow this code to be used in a long-running authentication daemon
  52. (for Courier).  authldap_get renamed to authldapcommon, will initialize and
  53. maintain a persistent connection.  Password checking moved entirely to
  54. authldap.c.  authldapclose() will unbind and close the connection.
  55. connection gets closed and reopened automatically after a protocol error.
  56. error return from authldapcommon will indicate whether this is a transient,
  57. or a permanent failure.
  58. authldap_free_config removed - no longer required.
  59. */
  60. #if HAVE_CONFIG_H
  61. #include "config.h"
  62. #endif
  63. #include <stdio.h>
  64. #include <stdlib.h>
  65. #include <ctype.h>
  66. #include <string.h>
  67. #include <errno.h>
  68. #include <pwd.h>
  69. #include <grp.h>
  70. #if HAVE_UNISTD_H
  71. #include <unistd.h>
  72. #endif
  73. #if HAVE_LBER_H
  74. #include <lber.h>
  75. #endif
  76. #if HAVE_LDAP_H
  77. #include <ldap.h>
  78. #endif
  79. #if HAVE_SYS_TIME_H
  80. #include <sys/time.h>
  81. #endif
  82. #if HAVE_SYS_STAT_H
  83. #include <sys/stat.h>
  84. #endif
  85. #if HAVE_SYSLOG_H
  86. #include <syslog.h>
  87. #else
  88. #define syslog(a,b)
  89. #endif
  90. #include "authldap.h"
  91. #include "auth.h"
  92. #include "authldaprc.h"
  93. #define DEBUG_LDAP 0
  94. struct ldap_info
  95. {
  96. const char *hostname;
  97. int   port;
  98. const char *binddn;
  99. const char *bindpw;
  100. const char *basedn;
  101. const char *mail;
  102. const char *domain;
  103. uid_t uid;
  104. gid_t gid;
  105. int   timeout;
  106. int   authbind;
  107. int authbind_ok;
  108. };
  109. static int read_env(const char *env, const char **copy,
  110. const char *err, int needit, const char *value_default);
  111. static void copy_value(LDAP *ld, LDAPMessage *entry, const char *attribut,
  112. char **copy);
  113. /*
  114.  * Function: read_env
  115.  * Copy the environnement $env and copy to $copy if not null
  116.  * if needit is false, and env doesn't exist, copy $value_default to $copy
  117.  * INPUT:
  118.  *   $env: pointer to the environnement name
  119.  *   $copy: where the value go
  120.  *   $err: print a nice message when $env not_found and $needit is true
  121.  *   $needit: if $needit is true and $value not found, return a error
  122.  *   $value_default: the default value when $needit is false and $env doesn't exists
  123.  * OUTPUT:
  124.  *   boolean
  125.  */
  126. static int read_env(const char *env, const char **copy,
  127. const char *err, int needit, const char *value_default)
  128. {
  129. static char *ldapauth=0;
  130. static size_t ldapauth_size;
  131. size_t i;
  132. char *p;
  133. int l=strlen(env);
  134. if (!ldapauth)
  135. {
  136. FILE *f=fopen(AUTHLDAPRC, "r");
  137. struct stat buf;
  138. if (!f) return (0);
  139. if (fstat(fileno(f), &buf) ||
  140. (ldapauth=malloc(buf.st_size+2)) == 0)
  141. {
  142. fclose(f);
  143. return (0);
  144. }
  145. if (fread(ldapauth, buf.st_size, 1, f) != 1)
  146. {
  147. free(ldapauth);
  148. ldapauth=0;
  149. fclose(f);
  150. return (0);
  151. }
  152. ldapauth[ldapauth_size=buf.st_size]=0;
  153. for (i=0; i<ldapauth_size; i++)
  154. if (ldapauth[i] == 'n')
  155. ldapauth[i]=0;
  156. }
  157. for (i=0; i<ldapauth_size; )
  158. {
  159. p=ldapauth+i;
  160. if (memcmp(p, env, l) == 0 &&
  161. isspace((int)(unsigned char)p[l]))
  162. {
  163. p += l;
  164. while (*p && *p != 'n' &&
  165. isspace((int)(unsigned char)*p))
  166. ++p;
  167. break;
  168. }
  169. while (i < ldapauth_size)
  170. if (ldapauth[i++] == 0) break;
  171. }
  172. if (i < ldapauth_size)
  173. {
  174. *copy= p;
  175. return (1);
  176. }
  177. if (needit)
  178. {
  179. fprintf(stderr, "ERR: %sn",err);
  180. fflush(stderr);
  181. return 0;
  182. }
  183. *copy=0;
  184. if (value_default)
  185. *copy=value_default;
  186. return 1;
  187. }
  188. /*
  189.  * Function: authldap_read_config
  190.  *   Read Configuration from the environnement table
  191.  * INPUT:
  192.  *   $ldap: a structure where we place information
  193.  * OUTPUT:
  194.  *   boolean
  195.  */
  196. static int authldap_read_config(struct ldap_info *ldap)
  197. {
  198.   struct passwd *pwent;
  199.   struct group  *grent;
  200.   const char *p;
  201.   memset(ldap,0,sizeof(struct ldap_info));
  202.   if (!read_env("LDAP_SERVER",&ldap->hostname,"You need to specify a ldap server in config file",1,NULL))
  203.     return 0;
  204.   if (!read_env("LDAP_AUTHBIND", &p, "", 0, ""))
  205. return (0);
  206.   if (p)
  207.     sscanf(p,"%d",&ldap->authbind);
  208.   ldap->port=LDAP_PORT;
  209.   if (!read_env("LDAP_PORT", &p, "", 0, ""))
  210. return (0);
  211.   if (p)
  212.     sscanf(p,"%d",&ldap->port);
  213.   if (!read_env("LDAP_BASEDN",&ldap->basedn,"You need to specify a basedn in config file",1,NULL))
  214.     return 0;
  215.   if (!read_env("LDAP_BINDDN",&ldap->binddn,"You need to specify a BINDDN in config file",0,NULL))
  216.     return 0;
  217.   if (!read_env("LDAP_BINDPW",&ldap->bindpw,"You need to specify a password for the BINDDN in config file",0,NULL))
  218.     return 0;
  219.   if (!read_env("LDAP_MAIL",&ldap->mail,"You need to specify a attribute for mail in config file",0,"mail"))
  220.     return 0;
  221.   if (!read_env("LDAP_DOMAIN",&ldap->domain,"You need to specify a domain for mail in config file",0,"*"))
  222.     return 0;
  223.   p=0;
  224.   ldap->uid=0;
  225.   if (!read_env("LDAP_GLOB_UID", &p, "", 0, ""))
  226. return (0);
  227.   if (p && *p)
  228.   {
  229.   unsigned long n;
  230. if (sscanf(p, "%lu", &n) == 1)
  231. ldap->uid=(uid_t)n;
  232. else
  233. {
  234. pwent=getpwnam(p);
  235. if (!pwent)
  236. {
  237. syslog(LOG_DAEMON|LOG_CRIT,
  238. "authldap: INVALID LDAP_GLOB_UIDn");
  239. return (0);
  240. }
  241. ldap->uid=pwent->pw_uid;
  242. }
  243.   }
  244.   ldap->gid=0;
  245.   p=0;
  246.   if (!read_env("LDAP_GLOB_GID", &p, "", 0, ""))
  247. return (0);
  248.   if (p && *p)
  249.   {
  250.   unsigned long n;
  251. if (sscanf(p, "%lu", &n) == 1)
  252. ldap->gid=(gid_t)n;
  253. else
  254. {
  255. grent=getgrnam(p);
  256. if (!grent)
  257. {
  258. syslog(LOG_DAEMON|LOG_CRIT,
  259. "authldap: INVALID LDAP_GLOB_GIDn");
  260. return (0);
  261. }
  262. ldap->gid=grent->gr_gid;
  263. }
  264.   }
  265.   ldap->timeout=5;
  266.   p=0;
  267.   if (!read_env("LDAP_TIMEOUT", &p, "", 0, ""))
  268. return (0);
  269.   if (p)
  270.   {
  271.     sscanf(p,"%d",&ldap->timeout);
  272.   }
  273.   return 1;
  274. }
  275. /*
  276.  * Function: copy_value
  277.  *   Copy value from a LDAP attribute to $copy
  278.  * INPUT:
  279.  *   $ld:       the connection with the LDAP server
  280.  *   $entry:    the entry who contains attributes
  281.  *   $attribut: this attribut
  282.  *   $copy:     where data can go
  283.  * OUTPUT:
  284.  *   void
  285.  */
  286. static void copy_value(LDAP *ld, LDAPMessage *entry, const char *attribut,
  287. char **copy)
  288. {
  289.   char ** values;
  290.   values=ldap_get_values(ld,entry, (char *)attribut);
  291. if (values==NULL)
  292. {
  293. if (ld->ld_errno != LDAP_DECODING_ERROR)
  294. /* We didn't ask for this attribute */
  295. ldap_perror(ld,"ldap_get_values");
  296. *copy=NULL;
  297. return;
  298. }
  299.   /* We accept only attribute with one value */
  300. else if (ldap_count_values(values)!=1)
  301.  {
  302.  *copy=NULL;
  303.  }
  304.   else
  305.  {
  306.  *copy=strdup(values[0]);
  307.  }
  308.   ldap_value_free(values);
  309. }
  310. static struct ldap_info my_ldap;
  311. static LDAP *my_ldap_fp=0;
  312. void authldapclose()
  313. {
  314. if (my_ldap_fp)
  315. {
  316. ldap_unbind(my_ldap_fp);
  317. my_ldap_fp=0;
  318. }
  319. }
  320. static int ldaperror(int rc)
  321. {
  322. if (rc && !NAME_ERROR(rc))
  323. /* If there was a protocol error, close the connection */
  324. authldapclose();
  325. return (rc);
  326. }
  327. static LDAP *ldapconnect()
  328. {
  329. LDAP *p;
  330. #if DEBUG_LDAP
  331. printf("Hostname: %s:%dn",my_ldap.hostname,my_ldap.port);
  332. printf("UID:      %dn",my_ldap.uid);
  333. printf("GID:      %dn",my_ldap.gid);
  334. #endif
  335. p=ldap_open((char *)my_ldap.hostname,my_ldap.port);
  336. if (p==NULL)
  337. {
  338. #if HAVE_SYSLOG_H
  339. syslog(LOG_DAEMON|LOG_CRIT,
  340. "Cannot connect to LDAP server (%s:%d): %s",
  341. my_ldap.hostname, my_ldap.port, strerror(errno));
  342. #else
  343. fprintf(stderr,
  344. "Cannot connect to LDAP server (%s:%d): %s",
  345. my_ldap.hostname, my_ldap.port, strerror(errno));
  346. #endif
  347. }
  348. return (p);
  349. }
  350. static int ldapopen()
  351. {
  352. if (my_ldap_fp) return (0);
  353. if (authldap_read_config(&my_ldap) == 0)
  354. return (1);
  355. my_ldap_fp=ldapconnect();
  356. if (!my_ldap_fp)
  357. {
  358. return (1);
  359. }
  360.   /* Bind to server */
  361. #if DEBUG_LDAP
  362.   printf("BindDn:   %snBindPw:   %sn",my_ldap.binddn,my_ldap.bindpw);
  363. #endif
  364.   if (ldaperror(ldap_simple_bind_s(my_ldap_fp,
  365. (char *)my_ldap.binddn,
  366. (char *)my_ldap.bindpw)) != LDAP_SUCCESS)
  367.     {
  368.     const char *s=ldap_err2string(my_ldap_fp->ld_errno);
  369. #if HAVE_SYSLOG_H
  370. syslog(LOG_DAEMON|LOG_CRIT, "ldap_simple_bind_s failed: %s", s);
  371. #endif
  372. authldapclose();
  373. return (-1);
  374.     }
  375. return (0);
  376. }
  377. /*
  378.  * Function: authldapcommon
  379.  *   Get information from the LDAP server ($ldap) for this $user
  380.  * INPUT:
  381.  *   $user: the login name
  382.  *   $pass: the login password (NULL if we don't want to check the pw)
  383.  *   callback - callback function with filled in authentication info
  384.  *   arg - extra argument for the callback function.
  385.  * OUTPUT:
  386.  *   < 0 - authentication failure
  387.  *   > 0 - temporary failure
  388.  *   else return code from the callback function.
  389.  */
  390. int authldapcommon(const char *user, const char *pass,
  391.         int (*callback)(struct authinfo *, void *),
  392.                         void *arg)
  393. {
  394.   const char *attributes[10], *ldap_attributes[10];
  395.   
  396.   struct timeval timeout;
  397.   LDAPMessage *result;
  398.   LDAPMessage *entry;
  399.   char *filter, *dn;
  400.   int i, j;
  401. struct authinfo auth;
  402. char *homeDir=0;
  403. char *mailDir=0;
  404. char *userPassword=0;
  405. char *cryptPassword=0;
  406. char *cn=0;
  407. uid_t au;
  408. gid_t ag;
  409. int rc;
  410. char *quota=0;
  411. memset(&auth, 0, sizeof(auth));
  412.   if (ldapopen()) return (1);
  413.   if ((filter=malloc(strlen(my_ldap.mail)+strlen(user)+
  414. (my_ldap.domain ? strlen(my_ldap.domain):0)+
  415. sizeof ("(=@)"))) == 0)
  416.   {
  417.       perror("malloc");
  418.       return 1;
  419.   }
  420.   strcat(strcat(strcat(strcpy(filter, "("), my_ldap.mail), "="), user);
  421.   if ( strchr(user, '@') == 0)
  422.      strcat(strcat(filter, "@"), my_ldap.domain);
  423.   strcat(filter, ")");
  424.   timeout.tv_sec=my_ldap.timeout;
  425.   timeout.tv_usec=0;
  426. read_env("LDAP_HOMEDIR", &attributes[0], "", 0, "homeDir");
  427. read_env("LDAP_MAILDIR", &attributes[1], "", 0, 0);
  428. read_env("LDAP_FULLNAME", &attributes[2], "", 0, "cn");
  429. read_env("LDAP_CLEARPW", &attributes[3], "", 0, 0);
  430. read_env("LDAP_CRYPTPW", &attributes[4], "", 0, 0);
  431. read_env("LDAP_UID", &attributes[5], "", 0, 0);
  432. read_env("LDAP_GID", &attributes[6], "", 0, 0);
  433. attributes[7]=my_ldap.mail;
  434. read_env("LDAP_MAILDIRQUOTA", &attributes[8], "", 0, 0);
  435. j=0;
  436. for (i=0; i<9; i++)
  437. {
  438. if (attributes[i])
  439. ldap_attributes[j++]=attributes[i];
  440. }
  441. ldap_attributes[j]=0;
  442.   if (ldaperror(ldap_search_st(my_ldap_fp, (char *)my_ldap.basedn,LDAP_SCOPE_SUBTREE,
  443. filter, (char **)ldap_attributes, 0, &timeout, &result)
  444. != LDAP_SUCCESS))
  445.     {
  446.       free(filter);
  447. if (my_ldap_fp) return (1);
  448. return (-1);
  449.     }
  450.    free(filter);
  451.   /* If we are more than one result, reject */
  452.   if (ldap_count_entries(my_ldap_fp,result)!=1)
  453.     {
  454.       ldap_msgfree(result);
  455.       return -1;
  456.     }
  457. #if DEBUG_LDAP
  458.   printf("Nombre de r閟ulat:    %dn",ldap_count_entries(ld,result));
  459. #endif
  460.   dn = ldap_get_dn(my_ldap_fp, result);
  461. #if DEBUG_LDAP
  462.   printf("DN:    %sn",dn);
  463. #endif
  464.   if (dn == NULL) 
  465.     {
  466.       ldap_perror(my_ldap_fp, "ldap_get_dn");
  467.       return -1;
  468.     }
  469.   /* Get the pointer on this result */
  470.   entry=ldap_first_entry(my_ldap_fp,result);
  471.   if (entry==NULL)
  472.     {
  473.       ldap_perror(my_ldap_fp,"ldap_first_entry");
  474.       free(dn);
  475.       return -1;
  476.     }
  477.   /* Copy the directory and the password into struct */
  478.   copy_value(my_ldap_fp,entry,attributes[0],&homeDir);
  479.   if (attributes[1])
  480.   copy_value(my_ldap_fp,entry,attributes[1],&mailDir);
  481.   copy_value(my_ldap_fp,entry,attributes[2],&cn);
  482.   if (attributes[3])
  483.   copy_value(my_ldap_fp,entry,attributes[3],&userPassword);
  484.   if (attributes[4])
  485.   copy_value(my_ldap_fp,entry,attributes[4],&cryptPassword);
  486. au=my_ldap.uid;
  487. ag=my_ldap.gid;
  488.   if (attributes[5])
  489.   {
  490. char *p=0;
  491. unsigned long n;
  492. copy_value(my_ldap_fp, entry, attributes[5], &p);
  493. if (!p) return (0);
  494. if (sscanf(p, "%lu", &n) > 0)
  495. au= (uid_t)n;
  496. free(p);
  497.   }
  498.   if (attributes[6])
  499.   {
  500. char *p=0;
  501. unsigned long n;
  502. copy_value(my_ldap_fp, entry, attributes[6], &p);
  503. if (!p) return (0);
  504. if (sscanf(p, "%lu", &n) > 0)
  505. ag= (gid_t)n;
  506. free(p);
  507.   }
  508.   if (attributes[8])
  509.   copy_value(my_ldap_fp,entry,attributes[8],&quota);
  510. auth.sysusername=user;
  511. auth.sysuserid= &au;
  512. auth.sysgroupid= ag;
  513. auth.homedir=homeDir;
  514. auth.address=user;
  515. auth.fullname=cn;
  516. auth.maildir=mailDir;
  517. auth.clearpasswd=userPassword;
  518. auth.passwd=cryptPassword;
  519. auth.quota=quota;
  520. if (auth.sysusername == 0)
  521. auth.sysusername=auth.address="";
  522. if (homeDir == 0)
  523. auth.homedir="";
  524. rc=0;
  525. if (pass)
  526. {
  527. if (my_ldap.authbind) 
  528. {
  529. LDAP *bindp=ldapconnect();
  530. if (!bindp)
  531. rc=1;
  532. else
  533. {
  534. if (ldaperror(
  535. ldap_simple_bind_s(bindp, dn, (char *)pass)
  536. != LDAP_SUCCESS))
  537. {
  538. rc= my_ldap_fp ? 1:-1;
  539. }
  540. ldap_unbind(bindp);
  541. }
  542. }
  543. else
  544. {
  545. if (auth.clearpasswd)
  546. {
  547. if (strcmp(pass,auth.clearpasswd))
  548. rc= -1;
  549. }
  550. else
  551. {
  552. const char *p=auth.passwd;
  553. if (p && strncmp(p, "{crypt}", 7) == 0)
  554. p += 7; /* For authcheckpassword */
  555. if (!p || authcheckpassword(pass, p))
  556. rc= -1;
  557. }
  558. }
  559.         }
  560. free (dn);
  561. if (rc == 0)
  562. rc= (*callback)(&auth, arg);
  563. ldap_msgfree(result);
  564. if (homeDir) free(homeDir);
  565. if (mailDir) free(mailDir);
  566. if (userPassword) free(userPassword);
  567. if (cryptPassword) free(cryptPassword);
  568. if (cn) free(cn);
  569. if (quota) free(quota);
  570. return (rc);
  571. }