190792Sgshapiro/* 2261363Sgshapiro * Copyright (c) 2001-2003,2009 Proofpoint, Inc. and its suppliers. 390792Sgshapiro * All rights reserved. 490792Sgshapiro * 590792Sgshapiro * By using this file, you agree to the terms and conditions set 690792Sgshapiro * forth in the LICENSE file which can be found at the top level of 790792Sgshapiro * the sendmail distribution. 890792Sgshapiro */ 990792Sgshapiro 1090792Sgshapiro#include <sm/gen.h> 11266692SgshapiroSM_RCSID("@(#)$Id: mbdb.c,v 1.43 2014-01-08 17:03:15 ca Exp $") 1290792Sgshapiro 1390792Sgshapiro#include <sys/param.h> 1490792Sgshapiro 1590792Sgshapiro#include <ctype.h> 1690792Sgshapiro#include <errno.h> 1790792Sgshapiro#include <pwd.h> 1890792Sgshapiro#include <stdlib.h> 1990792Sgshapiro#include <setjmp.h> 20110560Sgshapiro#include <unistd.h> 2190792Sgshapiro 2290792Sgshapiro#include <sm/limits.h> 2390792Sgshapiro#include <sm/conf.h> 2490792Sgshapiro#include <sm/assert.h> 2590792Sgshapiro#include <sm/bitops.h> 2690792Sgshapiro#include <sm/errstring.h> 2790792Sgshapiro#include <sm/heap.h> 2890792Sgshapiro#include <sm/mbdb.h> 2990792Sgshapiro#include <sm/string.h> 3094334Sgshapiro# ifdef EX_OK 3194334Sgshapiro# undef EX_OK /* for SVr4.2 SMP */ 3294334Sgshapiro# endif /* EX_OK */ 3390792Sgshapiro#include <sm/sysexits.h> 3490792Sgshapiro 3590792Sgshapiro#if LDAPMAP 3690792Sgshapiro# if _LDAP_EXAMPLE_ 3790792Sgshapiro# include <sm/ldap.h> 3890792Sgshapiro# endif /* _LDAP_EXAMPLE_ */ 3990792Sgshapiro#endif /* LDAPMAP */ 4090792Sgshapiro 4190792Sgshapirotypedef struct 4290792Sgshapiro{ 4390792Sgshapiro char *mbdb_typename; 4490792Sgshapiro int (*mbdb_initialize) __P((char *)); 4590792Sgshapiro int (*mbdb_lookup) __P((char *name, SM_MBDB_T *user)); 4690792Sgshapiro void (*mbdb_terminate) __P((void)); 4790792Sgshapiro} SM_MBDB_TYPE_T; 4890792Sgshapiro 4990792Sgshapirostatic int mbdb_pw_initialize __P((char *)); 5090792Sgshapirostatic int mbdb_pw_lookup __P((char *name, SM_MBDB_T *user)); 5190792Sgshapirostatic void mbdb_pw_terminate __P((void)); 5290792Sgshapiro 5390792Sgshapiro#if LDAPMAP 5490792Sgshapiro# if _LDAP_EXAMPLE_ 5590792Sgshapirostatic struct sm_ldap_struct LDAPLMAP; 5690792Sgshapirostatic int mbdb_ldap_initialize __P((char *)); 5790792Sgshapirostatic int mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user)); 5890792Sgshapirostatic void mbdb_ldap_terminate __P((void)); 5990792Sgshapiro# endif /* _LDAP_EXAMPLE_ */ 6090792Sgshapiro#endif /* LDAPMAP */ 6190792Sgshapiro 6290792Sgshapirostatic SM_MBDB_TYPE_T SmMbdbTypes[] = 6390792Sgshapiro{ 6490792Sgshapiro { "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate }, 6590792Sgshapiro#if LDAPMAP 6690792Sgshapiro# if _LDAP_EXAMPLE_ 6790792Sgshapiro { "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate }, 6890792Sgshapiro# endif /* _LDAP_EXAMPLE_ */ 6990792Sgshapiro#endif /* LDAPMAP */ 7090792Sgshapiro { NULL, NULL, NULL, NULL } 7190792Sgshapiro}; 7290792Sgshapiro 7390792Sgshapirostatic SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0]; 7490792Sgshapiro 7590792Sgshapiro/* 7690792Sgshapiro** SM_MBDB_INITIALIZE -- specify which mailbox database to use 7790792Sgshapiro** 7890792Sgshapiro** If this function is not called, then the "pw" implementation 7990792Sgshapiro** is used by default; this implementation uses getpwnam(). 8090792Sgshapiro** 8190792Sgshapiro** Parameters: 8290792Sgshapiro** mbdb -- Which mailbox database to use. 8390792Sgshapiro** The argument has the form "name" or "name.arg". 8490792Sgshapiro** "pw" means use getpwnam(). 8590792Sgshapiro** 8690792Sgshapiro** Results: 8790792Sgshapiro** EX_OK on success, or an EX_* code on failure. 8890792Sgshapiro*/ 8990792Sgshapiro 9090792Sgshapiroint 9190792Sgshapirosm_mbdb_initialize(mbdb) 9290792Sgshapiro char *mbdb; 9390792Sgshapiro{ 9490792Sgshapiro size_t namelen; 9590792Sgshapiro int err; 9690792Sgshapiro char *name; 9790792Sgshapiro char *arg; 9890792Sgshapiro SM_MBDB_TYPE_T *t; 9990792Sgshapiro 10090792Sgshapiro SM_REQUIRE(mbdb != NULL); 10190792Sgshapiro 10290792Sgshapiro name = mbdb; 10390792Sgshapiro arg = strchr(mbdb, '.'); 10490792Sgshapiro if (arg == NULL) 10590792Sgshapiro namelen = strlen(name); 10690792Sgshapiro else 10790792Sgshapiro { 10890792Sgshapiro namelen = arg - name; 10990792Sgshapiro ++arg; 11090792Sgshapiro } 11190792Sgshapiro 11290792Sgshapiro for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t) 11390792Sgshapiro { 11490792Sgshapiro if (strlen(t->mbdb_typename) == namelen && 11590792Sgshapiro strncmp(name, t->mbdb_typename, namelen) == 0) 11690792Sgshapiro { 11798121Sgshapiro err = EX_OK; 11898121Sgshapiro if (t->mbdb_initialize != NULL) 11998121Sgshapiro err = t->mbdb_initialize(arg); 12090792Sgshapiro if (err == EX_OK) 12190792Sgshapiro SmMbdbType = t; 12290792Sgshapiro return err; 12390792Sgshapiro } 12490792Sgshapiro } 12590792Sgshapiro return EX_UNAVAILABLE; 12690792Sgshapiro} 12790792Sgshapiro 12890792Sgshapiro/* 12990792Sgshapiro** SM_MBDB_TERMINATE -- terminate connection to the mailbox database 13090792Sgshapiro** 13190792Sgshapiro** Because this function closes any cached file descriptors that 13290792Sgshapiro** are being held open for the connection to the mailbox database, 13390792Sgshapiro** it should be called for security reasons prior to dropping privileges 13490792Sgshapiro** and execing another process. 13590792Sgshapiro** 13690792Sgshapiro** Parameters: 13790792Sgshapiro** none. 13890792Sgshapiro** 13990792Sgshapiro** Results: 14090792Sgshapiro** none. 14190792Sgshapiro*/ 14290792Sgshapiro 14390792Sgshapirovoid 14490792Sgshapirosm_mbdb_terminate() 14590792Sgshapiro{ 14698121Sgshapiro if (SmMbdbType->mbdb_terminate != NULL) 14798121Sgshapiro SmMbdbType->mbdb_terminate(); 14890792Sgshapiro} 14990792Sgshapiro 15090792Sgshapiro/* 15190792Sgshapiro** SM_MBDB_LOOKUP -- look up a local mail recipient, given name 15290792Sgshapiro** 15390792Sgshapiro** Parameters: 15490792Sgshapiro** name -- name of local mail recipient 15590792Sgshapiro** user -- pointer to structure to fill in on success 15690792Sgshapiro** 15790792Sgshapiro** Results: 15890792Sgshapiro** On success, fill in *user and return EX_OK. 15990792Sgshapiro** If the user does not exist, return EX_NOUSER. 16090792Sgshapiro** If a temporary failure (eg, a network failure) occurred, 16190792Sgshapiro** return EX_TEMPFAIL. Otherwise return EX_OSERR. 16290792Sgshapiro*/ 16390792Sgshapiro 16490792Sgshapiroint 16590792Sgshapirosm_mbdb_lookup(name, user) 16690792Sgshapiro char *name; 16790792Sgshapiro SM_MBDB_T *user; 16890792Sgshapiro{ 16998121Sgshapiro int ret = EX_NOUSER; 17098121Sgshapiro 17198121Sgshapiro if (SmMbdbType->mbdb_lookup != NULL) 17298121Sgshapiro ret = SmMbdbType->mbdb_lookup(name, user); 17398121Sgshapiro return ret; 17490792Sgshapiro} 17590792Sgshapiro 17690792Sgshapiro/* 17790792Sgshapiro** SM_MBDB_FROMPW -- copy from struct pw to SM_MBDB_T 17890792Sgshapiro** 17990792Sgshapiro** Parameters: 18090792Sgshapiro** user -- destination user information structure 18190792Sgshapiro** pw -- source passwd structure 18290792Sgshapiro** 18390792Sgshapiro** Results: 18490792Sgshapiro** none. 18590792Sgshapiro*/ 18690792Sgshapiro 18790792Sgshapirovoid 18890792Sgshapirosm_mbdb_frompw(user, pw) 18990792Sgshapiro SM_MBDB_T *user; 19090792Sgshapiro struct passwd *pw; 19190792Sgshapiro{ 19290792Sgshapiro SM_REQUIRE(user != NULL); 19390792Sgshapiro (void) sm_strlcpy(user->mbdb_name, pw->pw_name, 19490792Sgshapiro sizeof(user->mbdb_name)); 19590792Sgshapiro user->mbdb_uid = pw->pw_uid; 19690792Sgshapiro user->mbdb_gid = pw->pw_gid; 19790792Sgshapiro sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname, 19890792Sgshapiro sizeof(user->mbdb_fullname)); 19990792Sgshapiro (void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir, 20090792Sgshapiro sizeof(user->mbdb_homedir)); 20190792Sgshapiro (void) sm_strlcpy(user->mbdb_shell, pw->pw_shell, 20290792Sgshapiro sizeof(user->mbdb_shell)); 20390792Sgshapiro} 20490792Sgshapiro 20590792Sgshapiro/* 20690792Sgshapiro** SM_PWFULLNAME -- build full name of user from pw_gecos field. 20790792Sgshapiro** 20890792Sgshapiro** This routine interprets the strange entry that would appear 20990792Sgshapiro** in the GECOS field of the password file. 21090792Sgshapiro** 21190792Sgshapiro** Parameters: 21290792Sgshapiro** gecos -- name to build. 21390792Sgshapiro** user -- the login name of this user (for &). 21490792Sgshapiro** buf -- place to put the result. 21590792Sgshapiro** buflen -- length of buf. 21690792Sgshapiro** 21790792Sgshapiro** Returns: 21890792Sgshapiro** none. 21990792Sgshapiro*/ 22090792Sgshapiro 22194334Sgshapiro#if _FFR_HANDLE_ISO8859_GECOS 22294334Sgshapirostatic char Latin1ToASCII[128] = 22394334Sgshapiro{ 22494334Sgshapiro 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 22594334Sgshapiro 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 22694334Sgshapiro 99, 80, 36, 89, 124, 36, 34, 99, 97, 60, 45, 45, 114, 45, 111, 42, 22794334Sgshapiro 50, 51, 39, 117, 80, 46, 44, 49, 111, 62, 42, 42, 42, 63, 65, 65, 22894334Sgshapiro 65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, 68, 78, 79, 22994334Sgshapiro 79, 79, 79, 79, 88, 79, 85, 85, 85, 85, 89, 80, 66, 97, 97, 97, 97, 23094334Sgshapiro 97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, 100, 110, 23194334Sgshapiro 111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, 112, 121 23294334Sgshapiro}; 23394334Sgshapiro#endif /* _FFR_HANDLE_ISO8859_GECOS */ 23494334Sgshapiro 23590792Sgshapirovoid 23690792Sgshapirosm_pwfullname(gecos, user, buf, buflen) 23790792Sgshapiro register char *gecos; 23890792Sgshapiro char *user; 23990792Sgshapiro char *buf; 24090792Sgshapiro size_t buflen; 24190792Sgshapiro{ 24290792Sgshapiro register char *p; 24390792Sgshapiro register char *bp = buf; 24490792Sgshapiro 24590792Sgshapiro if (*gecos == '*') 24690792Sgshapiro gecos++; 24790792Sgshapiro 24890792Sgshapiro /* copy gecos, interpolating & to be full name */ 24990792Sgshapiro for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 25090792Sgshapiro { 25190792Sgshapiro if (bp >= &buf[buflen - 1]) 25290792Sgshapiro { 25390792Sgshapiro /* buffer overflow -- just use login name */ 25490792Sgshapiro (void) sm_strlcpy(buf, user, buflen); 25590792Sgshapiro return; 25690792Sgshapiro } 25790792Sgshapiro if (*p == '&') 25890792Sgshapiro { 25990792Sgshapiro /* interpolate full name */ 26090792Sgshapiro (void) sm_strlcpy(bp, user, buflen - (bp - buf)); 26190792Sgshapiro *bp = toupper(*bp); 26290792Sgshapiro bp += strlen(bp); 26390792Sgshapiro } 26490792Sgshapiro else 26594334Sgshapiro { 26694334Sgshapiro#if _FFR_HANDLE_ISO8859_GECOS 26794334Sgshapiro if ((unsigned char) *p >= 128) 26894334Sgshapiro *bp++ = Latin1ToASCII[(unsigned char) *p - 128]; 26994334Sgshapiro else 27094334Sgshapiro#endif /* _FFR_HANDLE_ISO8859_GECOS */ 27194334Sgshapiro *bp++ = *p; 27294334Sgshapiro } 27390792Sgshapiro } 27490792Sgshapiro *bp = '\0'; 27590792Sgshapiro} 27690792Sgshapiro 27790792Sgshapiro/* 27890792Sgshapiro** /etc/passwd implementation. 27990792Sgshapiro*/ 28090792Sgshapiro 28190792Sgshapiro/* 28290792Sgshapiro** MBDB_PW_INITIALIZE -- initialize getpwnam() version 28390792Sgshapiro** 28490792Sgshapiro** Parameters: 28590792Sgshapiro** arg -- unused. 28690792Sgshapiro** 28790792Sgshapiro** Results: 28890792Sgshapiro** EX_OK. 28990792Sgshapiro*/ 29090792Sgshapiro 29190792Sgshapiro/* ARGSUSED0 */ 29290792Sgshapirostatic int 29390792Sgshapirombdb_pw_initialize(arg) 29490792Sgshapiro char *arg; 29590792Sgshapiro{ 29690792Sgshapiro return EX_OK; 29790792Sgshapiro} 29890792Sgshapiro 29990792Sgshapiro/* 30090792Sgshapiro** MBDB_PW_LOOKUP -- look up a local mail recipient, given name 30190792Sgshapiro** 30290792Sgshapiro** Parameters: 30390792Sgshapiro** name -- name of local mail recipient 30490792Sgshapiro** user -- pointer to structure to fill in on success 30590792Sgshapiro** 30690792Sgshapiro** Results: 30790792Sgshapiro** On success, fill in *user and return EX_OK. 30890792Sgshapiro** Failure: EX_NOUSER. 30990792Sgshapiro*/ 31090792Sgshapiro 31190792Sgshapirostatic int 31290792Sgshapirombdb_pw_lookup(name, user) 31390792Sgshapiro char *name; 31490792Sgshapiro SM_MBDB_T *user; 31590792Sgshapiro{ 31690792Sgshapiro struct passwd *pw; 31790792Sgshapiro 31890792Sgshapiro#ifdef HESIOD 31990792Sgshapiro /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ 32090792Sgshapiro { 32190792Sgshapiro char *p; 32290792Sgshapiro 32390792Sgshapiro for (p = name; *p != '\0'; p++) 32490792Sgshapiro if (!isascii(*p) || !isdigit(*p)) 32590792Sgshapiro break; 32690792Sgshapiro if (*p == '\0') 32790792Sgshapiro return EX_NOUSER; 32890792Sgshapiro } 32990792Sgshapiro#endif /* HESIOD */ 33090792Sgshapiro 33190792Sgshapiro errno = 0; 33290792Sgshapiro pw = getpwnam(name); 33390792Sgshapiro if (pw == NULL) 33490792Sgshapiro { 335261363Sgshapiro#if _FFR_USE_GETPWNAM_ERRNO 33690792Sgshapiro /* 337261363Sgshapiro ** Only enable this code iff 338261363Sgshapiro ** user unknown <-> getpwnam() == NULL && errno == 0 339261363Sgshapiro ** (i.e., errno unchanged); see the POSIX spec. 34090792Sgshapiro */ 341261363Sgshapiro 342261363Sgshapiro if (errno != 0) 34390792Sgshapiro return EX_TEMPFAIL; 344261363Sgshapiro#endif /* _FFR_USE_GETPWNAM_ERRNO */ 34590792Sgshapiro return EX_NOUSER; 34690792Sgshapiro } 34790792Sgshapiro 34890792Sgshapiro sm_mbdb_frompw(user, pw); 34990792Sgshapiro return EX_OK; 35090792Sgshapiro} 35190792Sgshapiro 35290792Sgshapiro/* 35390792Sgshapiro** MBDB_PW_TERMINATE -- terminate connection to the mailbox database 35490792Sgshapiro** 35590792Sgshapiro** Parameters: 35690792Sgshapiro** none. 35790792Sgshapiro** 35890792Sgshapiro** Results: 35990792Sgshapiro** none. 36090792Sgshapiro*/ 36190792Sgshapiro 36290792Sgshapirostatic void 36390792Sgshapirombdb_pw_terminate() 36490792Sgshapiro{ 36590792Sgshapiro endpwent(); 36690792Sgshapiro} 36790792Sgshapiro 36890792Sgshapiro#if LDAPMAP 36990792Sgshapiro# if _LDAP_EXAMPLE_ 37090792Sgshapiro/* 37190792Sgshapiro** LDAP example implementation based on RFC 2307, "An Approach for Using 37290792Sgshapiro** LDAP as a Network Information Service": 37390792Sgshapiro** 37490792Sgshapiro** ( nisSchema.1.0 NAME 'uidNumber' 37590792Sgshapiro** DESC 'An integer uniquely identifying a user in an 37690792Sgshapiro** administrative domain' 37790792Sgshapiro** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE ) 37890792Sgshapiro** 37990792Sgshapiro** ( nisSchema.1.1 NAME 'gidNumber' 38090792Sgshapiro** DESC 'An integer uniquely identifying a group in an 38190792Sgshapiro** administrative domain' 38290792Sgshapiro** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE ) 38390792Sgshapiro** 38490792Sgshapiro** ( nisSchema.1.2 NAME 'gecos' 38590792Sgshapiro** DESC 'The GECOS field; the common name' 38690792Sgshapiro** EQUALITY caseIgnoreIA5Match 38790792Sgshapiro** SUBSTRINGS caseIgnoreIA5SubstringsMatch 38890792Sgshapiro** SYNTAX 'IA5String' SINGLE-VALUE ) 38990792Sgshapiro** 39090792Sgshapiro** ( nisSchema.1.3 NAME 'homeDirectory' 39190792Sgshapiro** DESC 'The absolute path to the home directory' 39290792Sgshapiro** EQUALITY caseExactIA5Match 39390792Sgshapiro** SYNTAX 'IA5String' SINGLE-VALUE ) 39490792Sgshapiro** 39590792Sgshapiro** ( nisSchema.1.4 NAME 'loginShell' 39690792Sgshapiro** DESC 'The path to the login shell' 39790792Sgshapiro** EQUALITY caseExactIA5Match 39890792Sgshapiro** SYNTAX 'IA5String' SINGLE-VALUE ) 39990792Sgshapiro** 40090792Sgshapiro** ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY 40190792Sgshapiro** DESC 'Abstraction of an account with POSIX attributes' 40290792Sgshapiro** MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) 40390792Sgshapiro** MAY ( userPassword $ loginShell $ gecos $ description ) ) 40490792Sgshapiro** 40590792Sgshapiro*/ 40690792Sgshapiro 40790792Sgshapiro# define MBDB_LDAP_LABEL "MailboxDatabase" 40890792Sgshapiro 40990792Sgshapiro# ifndef MBDB_LDAP_FILTER 41090792Sgshapiro# define MBDB_LDAP_FILTER "(&(objectClass=posixAccount)(uid=%0))" 41190792Sgshapiro# endif /* MBDB_LDAP_FILTER */ 41290792Sgshapiro 41390792Sgshapiro# ifndef MBDB_DEFAULT_LDAP_BASEDN 41490792Sgshapiro# define MBDB_DEFAULT_LDAP_BASEDN NULL 41590792Sgshapiro# endif /* MBDB_DEFAULT_LDAP_BASEDN */ 41690792Sgshapiro 41790792Sgshapiro# ifndef MBDB_DEFAULT_LDAP_SERVER 41890792Sgshapiro# define MBDB_DEFAULT_LDAP_SERVER NULL 41990792Sgshapiro# endif /* MBDB_DEFAULT_LDAP_SERVER */ 42090792Sgshapiro 42190792Sgshapiro/* 42290792Sgshapiro** MBDB_LDAP_INITIALIZE -- initialize LDAP version 42390792Sgshapiro** 42490792Sgshapiro** Parameters: 42590792Sgshapiro** arg -- LDAP specification 42690792Sgshapiro** 42790792Sgshapiro** Results: 42890792Sgshapiro** EX_OK on success, or an EX_* code on failure. 42990792Sgshapiro*/ 43090792Sgshapiro 43190792Sgshapirostatic int 43290792Sgshapirombdb_ldap_initialize(arg) 43390792Sgshapiro char *arg; 43490792Sgshapiro{ 43590792Sgshapiro sm_ldap_clear(&LDAPLMAP); 43690792Sgshapiro LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN; 437132943Sgshapiro LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER; 43890792Sgshapiro LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER; 43990792Sgshapiro 44090792Sgshapiro /* Only want one match */ 44190792Sgshapiro LDAPLMAP.ldap_sizelimit = 1; 44290792Sgshapiro 443132943Sgshapiro /* interpolate new ldap_base and ldap_host from arg if given */ 44490792Sgshapiro if (arg != NULL && *arg != '\0') 44590792Sgshapiro { 44690792Sgshapiro char *new; 44790792Sgshapiro char *sep; 44890792Sgshapiro size_t len; 44990792Sgshapiro 45090792Sgshapiro len = strlen(arg) + 1; 45190792Sgshapiro new = sm_malloc(len); 45290792Sgshapiro if (new == NULL) 45390792Sgshapiro return EX_TEMPFAIL; 45490792Sgshapiro (void) sm_strlcpy(new, arg, len); 45590792Sgshapiro sep = strrchr(new, '@'); 45690792Sgshapiro if (sep != NULL) 45790792Sgshapiro { 45890792Sgshapiro *sep++ = '\0'; 459132943Sgshapiro LDAPLMAP.ldap_host = sep; 46090792Sgshapiro } 46190792Sgshapiro LDAPLMAP.ldap_base = new; 46290792Sgshapiro } 46390792Sgshapiro return EX_OK; 46490792Sgshapiro} 46590792Sgshapiro 46690792Sgshapiro 46790792Sgshapiro/* 46890792Sgshapiro** MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name 46990792Sgshapiro** 47090792Sgshapiro** Parameters: 47190792Sgshapiro** name -- name of local mail recipient 47290792Sgshapiro** user -- pointer to structure to fill in on success 47390792Sgshapiro** 47490792Sgshapiro** Results: 47590792Sgshapiro** On success, fill in *user and return EX_OK. 47690792Sgshapiro** Failure: EX_NOUSER. 47790792Sgshapiro*/ 47890792Sgshapiro 47990792Sgshapiro#define NEED_FULLNAME 0x01 48090792Sgshapiro#define NEED_HOMEDIR 0x02 48190792Sgshapiro#define NEED_SHELL 0x04 48290792Sgshapiro#define NEED_UID 0x08 48390792Sgshapiro#define NEED_GID 0x10 48490792Sgshapiro 48590792Sgshapirostatic int 48690792Sgshapirombdb_ldap_lookup(name, user) 48790792Sgshapiro char *name; 48890792Sgshapiro SM_MBDB_T *user; 48990792Sgshapiro{ 49090792Sgshapiro int msgid; 49190792Sgshapiro int need; 49290792Sgshapiro int ret; 49390792Sgshapiro int save_errno; 49490792Sgshapiro LDAPMessage *entry; 49590792Sgshapiro BerElement *ber; 49690792Sgshapiro char *attr = NULL; 49790792Sgshapiro 49890792Sgshapiro if (strlen(name) >= sizeof(user->mbdb_name)) 49990792Sgshapiro { 50090792Sgshapiro errno = EINVAL; 50190792Sgshapiro return EX_NOUSER; 50290792Sgshapiro } 50390792Sgshapiro 50490792Sgshapiro if (LDAPLMAP.ldap_filter == NULL) 50590792Sgshapiro { 50690792Sgshapiro /* map not initialized, but don't have arg here */ 50790792Sgshapiro errno = EFAULT; 50890792Sgshapiro return EX_TEMPFAIL; 50990792Sgshapiro } 51090792Sgshapiro 511110560Sgshapiro if (LDAPLMAP.ldap_pid != getpid()) 512110560Sgshapiro { 513110560Sgshapiro /* re-open map in this child process */ 514110560Sgshapiro LDAPLMAP.ldap_ld = NULL; 515110560Sgshapiro } 516110560Sgshapiro 51790792Sgshapiro if (LDAPLMAP.ldap_ld == NULL) 51890792Sgshapiro { 51990792Sgshapiro /* map not open, try to open now */ 52090792Sgshapiro if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP)) 52190792Sgshapiro return EX_TEMPFAIL; 52290792Sgshapiro } 52390792Sgshapiro 52490792Sgshapiro sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP); 52590792Sgshapiro msgid = sm_ldap_search(&LDAPLMAP, name); 52690792Sgshapiro if (msgid == -1) 52790792Sgshapiro { 52890792Sgshapiro save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE; 52990792Sgshapiro# ifdef LDAP_SERVER_DOWN 53090792Sgshapiro if (errno == LDAP_SERVER_DOWN) 53190792Sgshapiro { 53290792Sgshapiro /* server disappeared, try reopen on next search */ 53390792Sgshapiro sm_ldap_close(&LDAPLMAP); 53490792Sgshapiro } 53590792Sgshapiro# endif /* LDAP_SERVER_DOWN */ 53690792Sgshapiro errno = save_errno; 53790792Sgshapiro return EX_TEMPFAIL; 53890792Sgshapiro } 53990792Sgshapiro 54090792Sgshapiro /* Get results */ 54190792Sgshapiro ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1, 54290792Sgshapiro (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL : 54390792Sgshapiro &(LDAPLMAP.ldap_timeout)), 54490792Sgshapiro &(LDAPLMAP.ldap_res)); 54590792Sgshapiro 54690792Sgshapiro if (ret != LDAP_RES_SEARCH_RESULT && 54790792Sgshapiro ret != LDAP_RES_SEARCH_ENTRY) 54890792Sgshapiro { 54990792Sgshapiro if (ret == 0) 55090792Sgshapiro errno = ETIMEDOUT; 55190792Sgshapiro else 55290792Sgshapiro errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); 55390792Sgshapiro ret = EX_TEMPFAIL; 55490792Sgshapiro goto abort; 55590792Sgshapiro } 55690792Sgshapiro 55790792Sgshapiro entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res); 55890792Sgshapiro if (entry == NULL) 55990792Sgshapiro { 560203004Sgshapiro int rc; 561203004Sgshapiro 562203004Sgshapiro /* 563203004Sgshapiro ** We may have gotten an LDAP_RES_SEARCH_RESULT response 564203004Sgshapiro ** with an error inside it, so we have to extract that 565203004Sgshapiro ** with ldap_parse_result(). This can happen when talking 566203004Sgshapiro ** to an LDAP proxy whose backend has gone down. 567203004Sgshapiro */ 568203004Sgshapiro 569203004Sgshapiro save_errno = ldap_parse_result(LDAPLMAP.ldap_ld, 570203004Sgshapiro LDAPLMAP.ldap_res, &rc, NULL, 571203004Sgshapiro NULL, NULL, NULL, 0); 57290792Sgshapiro if (save_errno == LDAP_SUCCESS) 573203004Sgshapiro save_errno = rc; 574203004Sgshapiro if (save_errno == LDAP_SUCCESS) 57590792Sgshapiro { 57690792Sgshapiro errno = ENOENT; 57790792Sgshapiro ret = EX_NOUSER; 57890792Sgshapiro } 57990792Sgshapiro else 58090792Sgshapiro { 58190792Sgshapiro errno = save_errno; 58290792Sgshapiro ret = EX_TEMPFAIL; 58390792Sgshapiro } 58490792Sgshapiro goto abort; 58590792Sgshapiro } 58690792Sgshapiro 58790792Sgshapiro# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 58890792Sgshapiro /* 58990792Sgshapiro ** Reset value to prevent lingering 59090792Sgshapiro ** LDAP_DECODING_ERROR due to 59190792Sgshapiro ** OpenLDAP 1.X's hack (see below) 59290792Sgshapiro */ 59390792Sgshapiro 59490792Sgshapiro LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS; 59590792Sgshapiro# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 59690792Sgshapiro 59790792Sgshapiro ret = EX_OK; 59890792Sgshapiro need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID; 59990792Sgshapiro for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber); 60090792Sgshapiro attr != NULL; 60190792Sgshapiro attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber)) 60290792Sgshapiro { 60390792Sgshapiro char **vals; 60490792Sgshapiro 60590792Sgshapiro vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr); 60690792Sgshapiro if (vals == NULL) 60790792Sgshapiro { 60890792Sgshapiro errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); 60990792Sgshapiro if (errno == LDAP_SUCCESS) 61090792Sgshapiro { 61190792Sgshapiro ldap_memfree(attr); 61290792Sgshapiro continue; 61390792Sgshapiro } 61490792Sgshapiro 61590792Sgshapiro /* Must be an error */ 61690792Sgshapiro errno += E_LDAPBASE; 61790792Sgshapiro ret = EX_TEMPFAIL; 61890792Sgshapiro goto abort; 61990792Sgshapiro } 62090792Sgshapiro 62190792Sgshapiro# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 62290792Sgshapiro /* 62390792Sgshapiro ** Reset value to prevent lingering 62490792Sgshapiro ** LDAP_DECODING_ERROR due to 62590792Sgshapiro ** OpenLDAP 1.X's hack (see below) 62690792Sgshapiro */ 62790792Sgshapiro 62890792Sgshapiro LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS; 62990792Sgshapiro# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 63090792Sgshapiro 63190792Sgshapiro if (vals[0] == NULL || vals[0][0] == '\0') 63290792Sgshapiro goto skip; 63390792Sgshapiro 63490792Sgshapiro if (strcasecmp(attr, "gecos") == 0) 63590792Sgshapiro { 63690792Sgshapiro if (!bitset(NEED_FULLNAME, need) || 63790792Sgshapiro strlen(vals[0]) >= sizeof(user->mbdb_fullname)) 63890792Sgshapiro goto skip; 63990792Sgshapiro 64090792Sgshapiro sm_pwfullname(vals[0], name, user->mbdb_fullname, 64190792Sgshapiro sizeof(user->mbdb_fullname)); 64290792Sgshapiro need &= ~NEED_FULLNAME; 64390792Sgshapiro } 64490792Sgshapiro else if (strcasecmp(attr, "homeDirectory") == 0) 64590792Sgshapiro { 64690792Sgshapiro if (!bitset(NEED_HOMEDIR, need) || 64790792Sgshapiro strlen(vals[0]) >= sizeof(user->mbdb_homedir)) 64890792Sgshapiro goto skip; 64990792Sgshapiro 65090792Sgshapiro (void) sm_strlcpy(user->mbdb_homedir, vals[0], 65190792Sgshapiro sizeof(user->mbdb_homedir)); 65290792Sgshapiro need &= ~NEED_HOMEDIR; 65390792Sgshapiro } 65490792Sgshapiro else if (strcasecmp(attr, "loginShell") == 0) 65590792Sgshapiro { 65690792Sgshapiro if (!bitset(NEED_SHELL, need) || 65790792Sgshapiro strlen(vals[0]) >= sizeof(user->mbdb_shell)) 65890792Sgshapiro goto skip; 65990792Sgshapiro 66090792Sgshapiro (void) sm_strlcpy(user->mbdb_shell, vals[0], 66190792Sgshapiro sizeof(user->mbdb_shell)); 66290792Sgshapiro need &= ~NEED_SHELL; 66390792Sgshapiro } 66490792Sgshapiro else if (strcasecmp(attr, "uidNumber") == 0) 66590792Sgshapiro { 66690792Sgshapiro char *p; 66790792Sgshapiro 66890792Sgshapiro if (!bitset(NEED_UID, need)) 66990792Sgshapiro goto skip; 67090792Sgshapiro 67190792Sgshapiro for (p = vals[0]; *p != '\0'; p++) 67290792Sgshapiro { 67390792Sgshapiro /* allow negative numbers */ 67490792Sgshapiro if (p == vals[0] && *p == '-') 67590792Sgshapiro { 67690792Sgshapiro /* but not simply '-' */ 67790792Sgshapiro if (*(p + 1) == '\0') 67890792Sgshapiro goto skip; 67990792Sgshapiro } 68090792Sgshapiro else if (!isascii(*p) || !isdigit(*p)) 68190792Sgshapiro goto skip; 68290792Sgshapiro } 68390792Sgshapiro user->mbdb_uid = atoi(vals[0]); 68490792Sgshapiro need &= ~NEED_UID; 68590792Sgshapiro } 68690792Sgshapiro else if (strcasecmp(attr, "gidNumber") == 0) 68790792Sgshapiro { 68890792Sgshapiro char *p; 68990792Sgshapiro 69090792Sgshapiro if (!bitset(NEED_GID, need)) 69190792Sgshapiro goto skip; 69290792Sgshapiro 69390792Sgshapiro for (p = vals[0]; *p != '\0'; p++) 69490792Sgshapiro { 69590792Sgshapiro /* allow negative numbers */ 69690792Sgshapiro if (p == vals[0] && *p == '-') 69790792Sgshapiro { 69890792Sgshapiro /* but not simply '-' */ 69990792Sgshapiro if (*(p + 1) == '\0') 70090792Sgshapiro goto skip; 70190792Sgshapiro } 70290792Sgshapiro else if (!isascii(*p) || !isdigit(*p)) 70390792Sgshapiro goto skip; 70490792Sgshapiro } 70590792Sgshapiro user->mbdb_gid = atoi(vals[0]); 70690792Sgshapiro need &= ~NEED_GID; 70790792Sgshapiro } 70890792Sgshapiro 70990792Sgshapiroskip: 71090792Sgshapiro ldap_value_free(vals); 71190792Sgshapiro ldap_memfree(attr); 71290792Sgshapiro } 71390792Sgshapiro 71490792Sgshapiro errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); 71590792Sgshapiro 71690792Sgshapiro /* 71790792Sgshapiro ** We check errno != LDAP_DECODING_ERROR since 71890792Sgshapiro ** OpenLDAP 1.X has a very ugly *undocumented* 71990792Sgshapiro ** hack of returning this error code from 72090792Sgshapiro ** ldap_next_attribute() if the library freed the 72190792Sgshapiro ** ber attribute. See: 72290792Sgshapiro ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html 72390792Sgshapiro */ 72490792Sgshapiro 72590792Sgshapiro if (errno != LDAP_SUCCESS && 72690792Sgshapiro errno != LDAP_DECODING_ERROR) 72790792Sgshapiro { 72890792Sgshapiro /* Must be an error */ 72990792Sgshapiro errno += E_LDAPBASE; 73090792Sgshapiro ret = EX_TEMPFAIL; 73190792Sgshapiro goto abort; 73290792Sgshapiro } 73390792Sgshapiro 73490792Sgshapiro abort: 73590792Sgshapiro save_errno = errno; 73690792Sgshapiro if (attr != NULL) 73790792Sgshapiro { 73890792Sgshapiro ldap_memfree(attr); 73990792Sgshapiro attr = NULL; 74090792Sgshapiro } 74190792Sgshapiro if (LDAPLMAP.ldap_res != NULL) 74290792Sgshapiro { 74390792Sgshapiro ldap_msgfree(LDAPLMAP.ldap_res); 74490792Sgshapiro LDAPLMAP.ldap_res = NULL; 74590792Sgshapiro } 74690792Sgshapiro if (ret == EX_OK) 74790792Sgshapiro { 74890792Sgshapiro if (need == 0) 74990792Sgshapiro { 75090792Sgshapiro (void) sm_strlcpy(user->mbdb_name, name, 75190792Sgshapiro sizeof(user->mbdb_name)); 75290792Sgshapiro save_errno = 0; 75390792Sgshapiro } 75490792Sgshapiro else 75590792Sgshapiro { 75690792Sgshapiro ret = EX_NOUSER; 75790792Sgshapiro save_errno = EINVAL; 75890792Sgshapiro } 75990792Sgshapiro } 76090792Sgshapiro errno = save_errno; 76190792Sgshapiro return ret; 76290792Sgshapiro} 76390792Sgshapiro 76490792Sgshapiro/* 76590792Sgshapiro** MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database 76690792Sgshapiro** 76790792Sgshapiro** Parameters: 76890792Sgshapiro** none. 76990792Sgshapiro** 77090792Sgshapiro** Results: 77190792Sgshapiro** none. 77290792Sgshapiro*/ 77390792Sgshapiro 77490792Sgshapirostatic void 77590792Sgshapirombdb_ldap_terminate() 77690792Sgshapiro{ 77790792Sgshapiro sm_ldap_close(&LDAPLMAP); 77890792Sgshapiro} 77990792Sgshapiro# endif /* _LDAP_EXAMPLE_ */ 78090792Sgshapiro#endif /* LDAPMAP */ 781