mbdb.c revision 261363
1101043Sdes/* 2228991Suqs * Copyright (c) 2001-2003,2009 Proofpoint, Inc. and its suppliers. 3101043Sdes * All rights reserved. 4101043Sdes * 5101043Sdes * By using this file, you agree to the terms and conditions set 6101043Sdes * forth in the LICENSE file which can be found at the top level of 7101043Sdes * the sendmail distribution. 8101043Sdes */ 9101043Sdes 10101043Sdes#include <sm/gen.h> 11101043SdesSM_RCSID("@(#)$Id: mbdb.c,v 1.43 2014/01/08 17:03:15 ca Exp $") 12101043Sdes 13101043Sdes#include <sys/param.h> 14101043Sdes 15101043Sdes#include <ctype.h> 16101043Sdes#include <errno.h> 17101043Sdes#include <pwd.h> 18101043Sdes#include <stdlib.h> 19101043Sdes#include <setjmp.h> 20101043Sdes#include <unistd.h> 21101043Sdes 22101043Sdes#include <sm/limits.h> 23101043Sdes#include <sm/conf.h> 24101043Sdes#include <sm/assert.h> 25101043Sdes#include <sm/bitops.h> 26101043Sdes#include <sm/errstring.h> 27101043Sdes#include <sm/heap.h> 28101043Sdes#include <sm/mbdb.h> 29101043Sdes#include <sm/string.h> 30101043Sdes# ifdef EX_OK 31101043Sdes# undef EX_OK /* for SVr4.2 SMP */ 32101043Sdes# endif /* EX_OK */ 33101043Sdes#include <sm/sysexits.h> 34101043Sdes 35101043Sdes#if LDAPMAP 36101043Sdes# if _LDAP_EXAMPLE_ 37101043Sdes# include <sm/ldap.h> 38101043Sdes# endif /* _LDAP_EXAMPLE_ */ 39101043Sdes#endif /* LDAPMAP */ 40101043Sdes 41101043Sdestypedef struct 42101163Sdes{ 43101163Sdes char *mbdb_typename; 44101043Sdes int (*mbdb_initialize) __P((char *)); 45101043Sdes int (*mbdb_lookup) __P((char *name, SM_MBDB_T *user)); 46284441Stuexen void (*mbdb_terminate) __P((void)); 47101043Sdes} SM_MBDB_TYPE_T; 48294225Stuexen 49294225Stuexenstatic int mbdb_pw_initialize __P((char *)); 50101043Sdesstatic int mbdb_pw_lookup __P((char *name, SM_MBDB_T *user)); 51101043Sdesstatic void mbdb_pw_terminate __P((void)); 52101043Sdes 53101043Sdes#if LDAPMAP 54101043Sdes# if _LDAP_EXAMPLE_ 55101043Sdesstatic struct sm_ldap_struct LDAPLMAP; 56101043Sdesstatic int mbdb_ldap_initialize __P((char *)); 57336039Sjamiestatic int mbdb_ldap_lookup __P((char *name, SM_MBDB_T *user)); 58101043Sdesstatic void mbdb_ldap_terminate __P((void)); 59101043Sdes# endif /* _LDAP_EXAMPLE_ */ 60101043Sdes#endif /* LDAPMAP */ 61101043Sdes 62101043Sdesstatic SM_MBDB_TYPE_T SmMbdbTypes[] = 63101043Sdes{ 64101043Sdes { "pw", mbdb_pw_initialize, mbdb_pw_lookup, mbdb_pw_terminate }, 65101043Sdes#if LDAPMAP 66285826Shrs# if _LDAP_EXAMPLE_ 67285826Shrs { "ldap", mbdb_ldap_initialize, mbdb_ldap_lookup, mbdb_ldap_terminate }, 68285826Shrs# endif /* _LDAP_EXAMPLE_ */ 69285826Shrs#endif /* LDAPMAP */ 70285826Shrs { NULL, NULL, NULL, NULL } 71101043Sdes}; 72101043Sdes 73101043Sdesstatic SM_MBDB_TYPE_T *SmMbdbType = &SmMbdbTypes[0]; 74235870Sthompsa 75179115Sbms/* 76101043Sdes** SM_MBDB_INITIALIZE -- specify which mailbox database to use 77294225Stuexen** 78101043Sdes** If this function is not called, then the "pw" implementation 79101043Sdes** is used by default; this implementation uses getpwnam(). 80101043Sdes** 81164201Skeramida** Parameters: 82164201Skeramida** mbdb -- Which mailbox database to use. 83164201Skeramida** The argument has the form "name" or "name.arg". 84284441Stuexen** "pw" means use getpwnam(). 85285826Shrs** 86164201Skeramida** Results: 87164201Skeramida** EX_OK on success, or an EX_* code on failure. 88164201Skeramida*/ 89164201Skeramida 90101043Sdesint 91101043Sdessm_mbdb_initialize(mbdb) 92101043Sdes char *mbdb; 93101043Sdes{ 94101043Sdes size_t namelen; 95101043Sdes int err; 96284441Stuexen char *name; 97284441Stuexen char *arg; 98284441Stuexen SM_MBDB_TYPE_T *t; 99284441Stuexen 100284441Stuexen SM_REQUIRE(mbdb != NULL); 101101043Sdes 102101043Sdes name = mbdb; 103101043Sdes arg = strchr(mbdb, '.'); 104230512Sjilles if (arg == NULL) 105101043Sdes namelen = strlen(name); 106101043Sdes else 107101043Sdes { 108294225Stuexen namelen = arg - name; 109101043Sdes ++arg; 110284441Stuexen } 111284441Stuexen 112101043Sdes for (t = SmMbdbTypes; t->mbdb_typename != NULL; ++t) 113101043Sdes { 114101043Sdes if (strlen(t->mbdb_typename) == namelen && 115101043Sdes strncmp(name, t->mbdb_typename, namelen) == 0) 116101043Sdes { 117101043Sdes err = EX_OK; 118101043Sdes if (t->mbdb_initialize != NULL) 119101043Sdes err = t->mbdb_initialize(arg); 120101043Sdes if (err == EX_OK) 121101043Sdes SmMbdbType = t; 122101043Sdes return err; 123101043Sdes } 124101043Sdes } 125101043Sdes return EX_UNAVAILABLE; 126101043Sdes} 127101043Sdes 128101043Sdes/* 129101043Sdes** SM_MBDB_TERMINATE -- terminate connection to the mailbox database 130101043Sdes** 131101043Sdes** Because this function closes any cached file descriptors that 132101043Sdes** are being held open for the connection to the mailbox database, 133101043Sdes** it should be called for security reasons prior to dropping privileges 134101043Sdes** and execing another process. 135164201Skeramida** 136164201Skeramida** Parameters: 137164201Skeramida** none. 138164201Skeramida** 139164201Skeramida** Results: 140164201Skeramida** none. 141164201Skeramida*/ 142164201Skeramida 143164201Skeramidavoid 144164201Skeramidasm_mbdb_terminate() 145164201Skeramida{ 146164201Skeramida if (SmMbdbType->mbdb_terminate != NULL) 147164201Skeramida SmMbdbType->mbdb_terminate(); 148164201Skeramida} 149164201Skeramida 150164201Skeramida/* 151164201Skeramida** SM_MBDB_LOOKUP -- look up a local mail recipient, given name 152285826Shrs** 153285826Shrs** Parameters: 154164201Skeramida** name -- name of local mail recipient 155164201Skeramida** user -- pointer to structure to fill in on success 156164201Skeramida** 157164201Skeramida** Results: 158164201Skeramida** On success, fill in *user and return EX_OK. 159164201Skeramida** If the user does not exist, return EX_NOUSER. 160164201Skeramida** If a temporary failure (eg, a network failure) occurred, 161164201Skeramida** return EX_TEMPFAIL. Otherwise return EX_OSERR. 162164201Skeramida*/ 163164201Skeramida 164164201Skeramidaint 165164201Skeramidasm_mbdb_lookup(name, user) 166164201Skeramida char *name; 167164201Skeramida SM_MBDB_T *user; 168164201Skeramida{ 169164201Skeramida int ret = EX_NOUSER; 170164201Skeramida 171164201Skeramida if (SmMbdbType->mbdb_lookup != NULL) 172164201Skeramida ret = SmMbdbType->mbdb_lookup(name, user); 173164201Skeramida return ret; 174164201Skeramida} 175164201Skeramida 176164201Skeramida/* 177164201Skeramida** SM_MBDB_FROMPW -- copy from struct pw to SM_MBDB_T 178164201Skeramida** 179164201Skeramida** Parameters: 180164201Skeramida** user -- destination user information structure 181164201Skeramida** pw -- source passwd structure 182164201Skeramida** 183285826Shrs** Results: 184164201Skeramida** none. 185164201Skeramida*/ 186164201Skeramida 187164201Skeramidavoid 188164201Skeramidasm_mbdb_frompw(user, pw) 189164201Skeramida SM_MBDB_T *user; 190164201Skeramida struct passwd *pw; 191164201Skeramida{ 192164201Skeramida SM_REQUIRE(user != NULL); 193164201Skeramida (void) sm_strlcpy(user->mbdb_name, pw->pw_name, 194164201Skeramida sizeof(user->mbdb_name)); 195101043Sdes user->mbdb_uid = pw->pw_uid; 196101043Sdes user->mbdb_gid = pw->pw_gid; 197101043Sdes sm_pwfullname(pw->pw_gecos, pw->pw_name, user->mbdb_fullname, 198101043Sdes sizeof(user->mbdb_fullname)); 199101043Sdes (void) sm_strlcpy(user->mbdb_homedir, pw->pw_dir, 200101043Sdes sizeof(user->mbdb_homedir)); 201101043Sdes (void) sm_strlcpy(user->mbdb_shell, pw->pw_shell, 202114853Srobert sizeof(user->mbdb_shell)); 203101043Sdes} 204101043Sdes 205101043Sdes/* 206101043Sdes** SM_PWFULLNAME -- build full name of user from pw_gecos field. 207101043Sdes** 208101043Sdes** This routine interprets the strange entry that would appear 209101043Sdes** in the GECOS field of the password file. 210101043Sdes** 211101043Sdes** Parameters: 212101043Sdes** gecos -- name to build. 213101043Sdes** user -- the login name of this user (for &). 214101043Sdes** buf -- place to put the result. 215101043Sdes** buflen -- length of buf. 216101043Sdes** 217101043Sdes** Returns: 218101043Sdes** none. 219101043Sdes*/ 220101043Sdes 221101043Sdes#if _FFR_HANDLE_ISO8859_GECOS 222101043Sdesstatic char Latin1ToASCII[128] = 223101043Sdes{ 224101043Sdes 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 225101043Sdes 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 226101043Sdes 99, 80, 36, 89, 124, 36, 34, 99, 97, 60, 45, 45, 114, 45, 111, 42, 227101043Sdes 50, 51, 39, 117, 80, 46, 44, 49, 111, 62, 42, 42, 42, 63, 65, 65, 228101043Sdes 65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, 68, 78, 79, 229101043Sdes 79, 79, 79, 79, 88, 79, 85, 85, 85, 85, 89, 80, 66, 97, 97, 97, 97, 230101043Sdes 97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, 100, 110, 231101043Sdes 111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, 112, 121 232101043Sdes}; 233101043Sdes#endif /* _FFR_HANDLE_ISO8859_GECOS */ 234101043Sdes 235101043Sdesvoid 236101043Sdessm_pwfullname(gecos, user, buf, buflen) 237101043Sdes register char *gecos; 238101043Sdes char *user; 239101043Sdes char *buf; 240285826Shrs size_t buflen; 241101043Sdes{ 242101043Sdes register char *p; 243101043Sdes register char *bp = buf; 244101043Sdes 245285826Shrs if (*gecos == '*') 246101043Sdes gecos++; 247101043Sdes 248285826Shrs /* copy gecos, interpolating & to be full name */ 249285826Shrs for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 250101043Sdes { 251101043Sdes if (bp >= &buf[buflen - 1]) 252101043Sdes { 253101043Sdes /* buffer overflow -- just use login name */ 254101043Sdes (void) sm_strlcpy(buf, user, buflen); 255285826Shrs return; 256285826Shrs } 257101043Sdes if (*p == '&') 258101043Sdes { 259101043Sdes /* interpolate full name */ 260285826Shrs (void) sm_strlcpy(bp, user, buflen - (bp - buf)); 261285826Shrs *bp = toupper(*bp); 262285826Shrs bp += strlen(bp); 263285826Shrs } 264285826Shrs else 265285826Shrs { 266101043Sdes#if _FFR_HANDLE_ISO8859_GECOS 267101043Sdes if ((unsigned char) *p >= 128) 268101043Sdes *bp++ = Latin1ToASCII[(unsigned char) *p - 128]; 269101043Sdes else 270101043Sdes#endif /* _FFR_HANDLE_ISO8859_GECOS */ 271101043Sdes *bp++ = *p; 272101043Sdes } 273284694Stuexen } 274284694Stuexen *bp = '\0'; 275284694Stuexen} 276284694Stuexen 277284694Stuexen/* 278284694Stuexen** /etc/passwd implementation. 279284694Stuexen*/ 280284694Stuexen 281284694Stuexen/* 282284694Stuexen** MBDB_PW_INITIALIZE -- initialize getpwnam() version 283284694Stuexen** 284284694Stuexen** Parameters: 285284694Stuexen** arg -- unused. 286284694Stuexen** 287284694Stuexen** Results: 288284694Stuexen** EX_OK. 289284694Stuexen*/ 290284694Stuexen 291284694Stuexen/* ARGSUSED0 */ 292284694Stuexenstatic int 293284441Stuexenmbdb_pw_initialize(arg) 294284441Stuexen char *arg; 295284441Stuexen{ 296284441Stuexen return EX_OK; 297284441Stuexen} 298284441Stuexen 299284441Stuexen/* 300284441Stuexen** MBDB_PW_LOOKUP -- look up a local mail recipient, given name 301284441Stuexen** 302284441Stuexen** Parameters: 303284441Stuexen** name -- name of local mail recipient 304284441Stuexen** user -- pointer to structure to fill in on success 305284441Stuexen** 306284441Stuexen** Results: 307284441Stuexen** On success, fill in *user and return EX_OK. 308284441Stuexen** Failure: EX_NOUSER. 309284441Stuexen*/ 310284441Stuexen 311284441Stuexenstatic int 312284441Stuexenmbdb_pw_lookup(name, user) 313284441Stuexen char *name; 314284441Stuexen SM_MBDB_T *user; 315284441Stuexen{ 316284441Stuexen struct passwd *pw; 317284441Stuexen 318284441Stuexen#ifdef HESIOD 319284441Stuexen /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ 320284441Stuexen { 321284441Stuexen char *p; 322284441Stuexen 323284441Stuexen for (p = name; *p != '\0'; p++) 324284441Stuexen if (!isascii(*p) || !isdigit(*p)) 325284441Stuexen break; 326284441Stuexen if (*p == '\0') 327284441Stuexen return EX_NOUSER; 328284441Stuexen } 329284441Stuexen#endif /* HESIOD */ 330284441Stuexen 331284441Stuexen errno = 0; 332284441Stuexen pw = getpwnam(name); 333284441Stuexen if (pw == NULL) 334284441Stuexen { 335284441Stuexen#if _FFR_USE_GETPWNAM_ERRNO 336294232Stuexen /* 337294231Stuexen ** Only enable this code iff 338294231Stuexen ** user unknown <-> getpwnam() == NULL && errno == 0 339294231Stuexen ** (i.e., errno unchanged); see the POSIX spec. 340284441Stuexen */ 341284441Stuexen 342284441Stuexen if (errno != 0) 343284441Stuexen return EX_TEMPFAIL; 344284441Stuexen#endif /* _FFR_USE_GETPWNAM_ERRNO */ 345284441Stuexen return EX_NOUSER; 346284441Stuexen } 347284441Stuexen 348284441Stuexen sm_mbdb_frompw(user, pw); 349284441Stuexen return EX_OK; 350284441Stuexen} 351284441Stuexen 352284441Stuexen/* 353284441Stuexen** MBDB_PW_TERMINATE -- terminate connection to the mailbox database 354284441Stuexen** 355284441Stuexen** Parameters: 356284441Stuexen** none. 357284441Stuexen** 358284441Stuexen** Results: 359284441Stuexen** none. 360284441Stuexen*/ 361284441Stuexen 362284441Stuexenstatic void 363284441Stuexenmbdb_pw_terminate() 364284441Stuexen{ 365284441Stuexen endpwent(); 366284441Stuexen} 367284441Stuexen 368284441Stuexen#if LDAPMAP 369284441Stuexen# if _LDAP_EXAMPLE_ 370284441Stuexen/* 371284441Stuexen** LDAP example implementation based on RFC 2307, "An Approach for Using 372284441Stuexen** LDAP as a Network Information Service": 373284441Stuexen** 374284441Stuexen** ( nisSchema.1.0 NAME 'uidNumber' 375284441Stuexen** DESC 'An integer uniquely identifying a user in an 376284441Stuexen** administrative domain' 377293290Sbdrewery** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE ) 378284441Stuexen** 379284441Stuexen** ( nisSchema.1.1 NAME 'gidNumber' 380284441Stuexen** DESC 'An integer uniquely identifying a group in an 381284441Stuexen** administrative domain' 382284441Stuexen** EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE ) 383284441Stuexen** 384284441Stuexen** ( nisSchema.1.2 NAME 'gecos' 385284441Stuexen** DESC 'The GECOS field; the common name' 386284441Stuexen** EQUALITY caseIgnoreIA5Match 387284441Stuexen** SUBSTRINGS caseIgnoreIA5SubstringsMatch 388284441Stuexen** SYNTAX 'IA5String' SINGLE-VALUE ) 389284441Stuexen** 390284441Stuexen** ( nisSchema.1.3 NAME 'homeDirectory' 391284441Stuexen** DESC 'The absolute path to the home directory' 392284441Stuexen** EQUALITY caseExactIA5Match 393284441Stuexen** SYNTAX 'IA5String' SINGLE-VALUE ) 394284441Stuexen** 395284441Stuexen** ( nisSchema.1.4 NAME 'loginShell' 396284441Stuexen** DESC 'The path to the login shell' 397284441Stuexen** EQUALITY caseExactIA5Match 398284441Stuexen** SYNTAX 'IA5String' SINGLE-VALUE ) 399284441Stuexen** 400284441Stuexen** ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY 401284441Stuexen** DESC 'Abstraction of an account with POSIX attributes' 402284441Stuexen** MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) 403284441Stuexen** MAY ( userPassword $ loginShell $ gecos $ description ) ) 404284441Stuexen** 405284441Stuexen*/ 406284441Stuexen 407284441Stuexen# define MBDB_LDAP_LABEL "MailboxDatabase" 408284694Stuexen 409284694Stuexen# ifndef MBDB_LDAP_FILTER 410284694Stuexen# define MBDB_LDAP_FILTER "(&(objectClass=posixAccount)(uid=%0))" 411284694Stuexen# endif /* MBDB_LDAP_FILTER */ 412284694Stuexen 413284694Stuexen# ifndef MBDB_DEFAULT_LDAP_BASEDN 414284694Stuexen# define MBDB_DEFAULT_LDAP_BASEDN NULL 415284694Stuexen# endif /* MBDB_DEFAULT_LDAP_BASEDN */ 416284694Stuexen 417284694Stuexen# ifndef MBDB_DEFAULT_LDAP_SERVER 418284694Stuexen# define MBDB_DEFAULT_LDAP_SERVER NULL 419284441Stuexen# endif /* MBDB_DEFAULT_LDAP_SERVER */ 420284441Stuexen 421284441Stuexen/* 422284441Stuexen** MBDB_LDAP_INITIALIZE -- initialize LDAP version 423284441Stuexen** 424284441Stuexen** Parameters: 425284441Stuexen** arg -- LDAP specification 426284441Stuexen** 427284441Stuexen** Results: 428284441Stuexen** EX_OK on success, or an EX_* code on failure. 429294231Stuexen*/ 430284441Stuexen 431284441Stuexenstatic int 432284441Stuexenmbdb_ldap_initialize(arg) 433284441Stuexen char *arg; 434284441Stuexen{ 435284441Stuexen sm_ldap_clear(&LDAPLMAP); 436284441Stuexen LDAPLMAP.ldap_base = MBDB_DEFAULT_LDAP_BASEDN; 437284441Stuexen LDAPLMAP.ldap_host = MBDB_DEFAULT_LDAP_SERVER; 438284441Stuexen LDAPLMAP.ldap_filter = MBDB_LDAP_FILTER; 439284441Stuexen 440284441Stuexen /* Only want one match */ 441284441Stuexen LDAPLMAP.ldap_sizelimit = 1; 442284441Stuexen 443284441Stuexen /* interpolate new ldap_base and ldap_host from arg if given */ 444284441Stuexen if (arg != NULL && *arg != '\0') 445284441Stuexen { 446284441Stuexen char *new; 447284441Stuexen char *sep; 448284441Stuexen size_t len; 449284441Stuexen 450284441Stuexen len = strlen(arg) + 1; 451284441Stuexen new = sm_malloc(len); 452284441Stuexen if (new == NULL) 453284441Stuexen return EX_TEMPFAIL; 454284441Stuexen (void) sm_strlcpy(new, arg, len); 455284441Stuexen sep = strrchr(new, '@'); 456284441Stuexen if (sep != NULL) 457284441Stuexen { 458284441Stuexen *sep++ = '\0'; 459284441Stuexen LDAPLMAP.ldap_host = sep; 460284441Stuexen } 461284441Stuexen LDAPLMAP.ldap_base = new; 462284441Stuexen } 463284441Stuexen return EX_OK; 464284441Stuexen} 465284441Stuexen 466284441Stuexen 467284441Stuexen/* 468284441Stuexen** MBDB_LDAP_LOOKUP -- look up a local mail recipient, given name 469284441Stuexen** 470293290Sbdrewery** Parameters: 471284441Stuexen** name -- name of local mail recipient 472284441Stuexen** user -- pointer to structure to fill in on success 473284441Stuexen** 474284441Stuexen** Results: 475284441Stuexen** On success, fill in *user and return EX_OK. 476284441Stuexen** Failure: EX_NOUSER. 477284441Stuexen*/ 478284441Stuexen 479284441Stuexen#define NEED_FULLNAME 0x01 480284441Stuexen#define NEED_HOMEDIR 0x02 481284441Stuexen#define NEED_SHELL 0x04 482284441Stuexen#define NEED_UID 0x08 483284441Stuexen#define NEED_GID 0x10 484284441Stuexen 485284441Stuexenstatic int 486284441Stuexenmbdb_ldap_lookup(name, user) 487284441Stuexen char *name; 488284441Stuexen SM_MBDB_T *user; 489284441Stuexen{ 490284441Stuexen int msgid; 491284441Stuexen int need; 492284441Stuexen int ret; 493284441Stuexen int save_errno; 494284441Stuexen LDAPMessage *entry; 495284441Stuexen BerElement *ber; 496284441Stuexen char *attr = NULL; 497284441Stuexen 498284441Stuexen if (strlen(name) >= sizeof(user->mbdb_name)) 499284441Stuexen { 500284441Stuexen errno = EINVAL; 501284441Stuexen return EX_NOUSER; 502284441Stuexen } 503284441Stuexen 504284441Stuexen if (LDAPLMAP.ldap_filter == NULL) 505284441Stuexen { 506284441Stuexen /* map not initialized, but don't have arg here */ 507284441Stuexen errno = EFAULT; 508284441Stuexen return EX_TEMPFAIL; 509284441Stuexen } 510284441Stuexen 511284441Stuexen if (LDAPLMAP.ldap_pid != getpid()) 512293290Sbdrewery { 513284441Stuexen /* re-open map in this child process */ 514284441Stuexen LDAPLMAP.ldap_ld = NULL; 515284441Stuexen } 516284441Stuexen 517284441Stuexen if (LDAPLMAP.ldap_ld == NULL) 518284441Stuexen { 519284441Stuexen /* map not open, try to open now */ 520284441Stuexen if (!sm_ldap_start(MBDB_LDAP_LABEL, &LDAPLMAP)) 521284441Stuexen return EX_TEMPFAIL; 522284694Stuexen } 523284694Stuexen 524284694Stuexen sm_ldap_setopts(LDAPLMAP.ldap_ld, &LDAPLMAP); 525284694Stuexen msgid = sm_ldap_search(&LDAPLMAP, name); 526284694Stuexen if (msgid == -1) 527284694Stuexen { 528284694Stuexen save_errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld) + E_LDAPBASE; 529284694Stuexen# ifdef LDAP_SERVER_DOWN 530284441Stuexen if (errno == LDAP_SERVER_DOWN) 531284441Stuexen { 532284441Stuexen /* server disappeared, try reopen on next search */ 533284441Stuexen sm_ldap_close(&LDAPLMAP); 534284441Stuexen } 535284441Stuexen# endif /* LDAP_SERVER_DOWN */ 536284441Stuexen errno = save_errno; 537284441Stuexen return EX_TEMPFAIL; 538284441Stuexen } 539101043Sdes 540101043Sdes /* Get results */ 541101043Sdes ret = ldap_result(LDAPLMAP.ldap_ld, msgid, 1, 542101043Sdes (LDAPLMAP.ldap_timeout.tv_sec == 0 ? NULL : 543101043Sdes &(LDAPLMAP.ldap_timeout)), 544101043Sdes &(LDAPLMAP.ldap_res)); 545101043Sdes 546101043Sdes if (ret != LDAP_RES_SEARCH_RESULT && 547284441Stuexen ret != LDAP_RES_SEARCH_ENTRY) 548101043Sdes { 549101043Sdes if (ret == 0) 550101043Sdes errno = ETIMEDOUT; 551294230Stuexen else 552101043Sdes errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); 553294230Stuexen ret = EX_TEMPFAIL; 554101043Sdes goto abort; 555101043Sdes } 556101043Sdes 557101043Sdes entry = ldap_first_entry(LDAPLMAP.ldap_ld, LDAPLMAP.ldap_res); 558101043Sdes if (entry == NULL) 559101043Sdes { 560101043Sdes int rc; 561101043Sdes 562101043Sdes /* 563101043Sdes ** We may have gotten an LDAP_RES_SEARCH_RESULT response 564101043Sdes ** with an error inside it, so we have to extract that 565101043Sdes ** with ldap_parse_result(). This can happen when talking 566101043Sdes ** to an LDAP proxy whose backend has gone down. 567101043Sdes */ 568138391Sru 569138391Sru save_errno = ldap_parse_result(LDAPLMAP.ldap_ld, 570138391Sru LDAPLMAP.ldap_res, &rc, NULL, 571138391Sru NULL, NULL, NULL, 0); 572101043Sdes if (save_errno == LDAP_SUCCESS) 573164201Skeramida save_errno = rc; 574101043Sdes if (save_errno == LDAP_SUCCESS) 575101043Sdes { 576101043Sdes errno = ENOENT; 577101043Sdes ret = EX_NOUSER; 578101043Sdes } 579101043Sdes else 580101043Sdes { 581101043Sdes errno = save_errno; 582101043Sdes ret = EX_TEMPFAIL; 583101043Sdes } 584101043Sdes goto abort; 585101043Sdes } 586138437Sru 587138437Sru# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 588230874Strociny /* 589101043Sdes ** Reset value to prevent lingering 590101043Sdes ** LDAP_DECODING_ERROR due to 591101043Sdes ** OpenLDAP 1.X's hack (see below) 592101043Sdes */ 593101220Srobert 594101220Srobert LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS; 595101043Sdes# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 596101043Sdes 597101043Sdes ret = EX_OK; 598101043Sdes need = NEED_FULLNAME|NEED_HOMEDIR|NEED_SHELL|NEED_UID|NEED_GID; 599101043Sdes for (attr = ldap_first_attribute(LDAPLMAP.ldap_ld, entry, &ber); 600101043Sdes attr != NULL; 601101043Sdes attr = ldap_next_attribute(LDAPLMAP.ldap_ld, entry, ber)) 602101043Sdes { 603101043Sdes char **vals; 604101220Srobert 605101043Sdes vals = ldap_get_values(LDAPLMAP.ldap_ld, entry, attr); 606101043Sdes if (vals == NULL) 607294226Stuexen { 608294226Stuexen errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); 609101043Sdes if (errno == LDAP_SUCCESS) 610101043Sdes { 611285826Shrs ldap_memfree(attr); 612101043Sdes continue; 613101043Sdes } 614101043Sdes 615101043Sdes /* Must be an error */ 616101043Sdes errno += E_LDAPBASE; 617237263Snp ret = EX_TEMPFAIL; 618101043Sdes goto abort; 619101043Sdes } 620138391Sru 621285826Shrs# if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 622101043Sdes /* 623101043Sdes ** Reset value to prevent lingering 624101043Sdes ** LDAP_DECODING_ERROR due to 625101043Sdes ** OpenLDAP 1.X's hack (see below) 626101043Sdes */ 627101043Sdes 628101043Sdes LDAPLMAP.ldap_ld->ld_errno = LDAP_SUCCESS; 629164201Skeramida# endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 630101043Sdes 631101043Sdes if (vals[0] == NULL || vals[0][0] == '\0') 632101043Sdes goto skip; 633101144Sdes 634101144Sdes if (strcasecmp(attr, "gecos") == 0) 635101144Sdes { 636101144Sdes if (!bitset(NEED_FULLNAME, need) || 637179115Sbms strlen(vals[0]) >= sizeof(user->mbdb_fullname)) 638179115Sbms goto skip; 639179115Sbms 640179115Sbms sm_pwfullname(vals[0], name, user->mbdb_fullname, 641179115Sbms sizeof(user->mbdb_fullname)); 642179115Sbms need &= ~NEED_FULLNAME; 643179115Sbms } 644101144Sdes else if (strcasecmp(attr, "homeDirectory") == 0) 645189637Srwatson { 646189637Srwatson if (!bitset(NEED_HOMEDIR, need) || 647101144Sdes strlen(vals[0]) >= sizeof(user->mbdb_homedir)) 648179115Sbms goto skip; 649179115Sbms 650179115Sbms (void) sm_strlcpy(user->mbdb_homedir, vals[0], 651179115Sbms sizeof(user->mbdb_homedir)); 652101144Sdes need &= ~NEED_HOMEDIR; 653101144Sdes } 654101144Sdes else if (strcasecmp(attr, "loginShell") == 0) 655101144Sdes { 656101144Sdes if (!bitset(NEED_SHELL, need) || 657285826Shrs strlen(vals[0]) >= sizeof(user->mbdb_shell)) 658101043Sdes goto skip; 659284441Stuexen 660284441Stuexen (void) sm_strlcpy(user->mbdb_shell, vals[0], 661284441Stuexen sizeof(user->mbdb_shell)); 662284441Stuexen need &= ~NEED_SHELL; 663101043Sdes } 664101043Sdes else if (strcasecmp(attr, "uidNumber") == 0) 665101043Sdes { 666101043Sdes char *p; 667284441Stuexen 668101043Sdes if (!bitset(NEED_UID, need)) 669284441Stuexen goto skip; 670101043Sdes 671101043Sdes for (p = vals[0]; *p != '\0'; p++) 672101043Sdes { 673284441Stuexen /* allow negative numbers */ 674189637Srwatson if (p == vals[0] && *p == '-') 675284441Stuexen { 676189637Srwatson /* but not simply '-' */ 677101043Sdes if (*(p + 1) == '\0') 678284441Stuexen goto skip; 679284441Stuexen } 680284441Stuexen else if (!isascii(*p) || !isdigit(*p)) 681284441Stuexen goto skip; 682101043Sdes } 683294225Stuexen user->mbdb_uid = atoi(vals[0]); 684294225Stuexen need &= ~NEED_UID; 685101043Sdes } 686101043Sdes else if (strcasecmp(attr, "gidNumber") == 0) 687101043Sdes { 688101043Sdes char *p; 689101043Sdes 690101043Sdes if (!bitset(NEED_GID, need)) 691101043Sdes goto skip; 692101043Sdes 693101043Sdes for (p = vals[0]; *p != '\0'; p++) 694101043Sdes { 695101043Sdes /* allow negative numbers */ 696101043Sdes if (p == vals[0] && *p == '-') 697101043Sdes { 698101043Sdes /* but not simply '-' */ 699101043Sdes if (*(p + 1) == '\0') 700284441Stuexen goto skip; 701101043Sdes } 702101043Sdes else if (!isascii(*p) || !isdigit(*p)) 703101043Sdes goto skip; 704101043Sdes } 705101043Sdes user->mbdb_gid = atoi(vals[0]); 706101043Sdes need &= ~NEED_GID; 707101043Sdes } 708101043Sdes 709101043Sdesskip: 710101043Sdes ldap_value_free(vals); 711101043Sdes ldap_memfree(attr); 712101043Sdes } 713101043Sdes 714101043Sdes errno = sm_ldap_geterrno(LDAPLMAP.ldap_ld); 715285826Shrs 716285826Shrs /* 717285826Shrs ** We check errno != LDAP_DECODING_ERROR since 718285826Shrs ** OpenLDAP 1.X has a very ugly *undocumented* 719101043Sdes ** hack of returning this error code from 720101043Sdes ** ldap_next_attribute() if the library freed the 721101043Sdes ** ber attribute. See: 722101043Sdes ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html 723101043Sdes */ 724101043Sdes 725101043Sdes if (errno != LDAP_SUCCESS && 726101043Sdes errno != LDAP_DECODING_ERROR) 727101043Sdes { 728101043Sdes /* Must be an error */ 729101043Sdes errno += E_LDAPBASE; 730101043Sdes ret = EX_TEMPFAIL; 731101043Sdes goto abort; 732230874Strociny } 733101043Sdes 734101043Sdes abort: 735101043Sdes save_errno = errno; 736101043Sdes if (attr != NULL) 737101220Srobert { 738285826Shrs ldap_memfree(attr); 739285826Shrs attr = NULL; 740285826Shrs } 741101043Sdes if (LDAPLMAP.ldap_res != NULL) 742101043Sdes { 743101043Sdes ldap_msgfree(LDAPLMAP.ldap_res); 744101043Sdes LDAPLMAP.ldap_res = NULL; 745101043Sdes } 746101043Sdes if (ret == EX_OK) 747101043Sdes { 748101043Sdes if (need == 0) 749101043Sdes { 750101220Srobert (void) sm_strlcpy(user->mbdb_name, name, 751101043Sdes sizeof(user->mbdb_name)); 752101043Sdes save_errno = 0; 753101043Sdes } 754285826Shrs else 755101043Sdes { 756101043Sdes ret = EX_NOUSER; 757101043Sdes save_errno = EINVAL; 758101144Sdes } 759101144Sdes } 760101144Sdes errno = save_errno; 761285826Shrs return ret; 762101043Sdes} 763284441Stuexen 764284441Stuexen/* 765284441Stuexen** MBDB_LDAP_TERMINATE -- terminate connection to the mailbox database 766284441Stuexen** 767101043Sdes** Parameters: 768101043Sdes** none. 769101043Sdes** 770101043Sdes** Results: 771101043Sdes** none. 772101043Sdes*/ 773284441Stuexen 774101220Srobertstatic void 775101043Sdesmbdb_ldap_terminate() 776284441Stuexen{ 777284441Stuexen sm_ldap_close(&LDAPLMAP); 778284441Stuexen} 779284441Stuexen# endif /* _LDAP_EXAMPLE_ */ 780284441Stuexen#endif /* LDAPMAP */ 781101043Sdes