1/* 2 * $Id: uam.c,v 1.35 2009-11-08 01:15:31 didg Exp $ 3 * 4 * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu) 5 * All Rights Reserved. See COPYRIGHT. 6 */ 7 8#ifdef HAVE_CONFIG_H 9#include "config.h" 10#endif /* HAVE_CONFIG_H */ 11 12#include <stdio.h> 13#include <stdlib.h> 14 15/* STDC check */ 16#if STDC_HEADERS 17#include <string.h> 18#else /* STDC_HEADERS */ 19#ifndef HAVE_STRCHR 20#define strchr index 21#define strrchr index 22#endif /* HAVE_STRCHR */ 23char *strchr (), *strrchr (); 24#ifndef HAVE_MEMCPY 25#define memcpy(d,s,n) bcopy ((s), (d), (n)) 26#define memmove(d,s,n) bcopy ((s), (d), (n)) 27#endif /* ! HAVE_MEMCPY */ 28#endif /* STDC_HEADERS */ 29 30#ifdef HAVE_UNISTD_H 31#include <unistd.h> 32#endif /* HAVE_UNISTD_H */ 33#ifdef HAVE_FCNTL_H 34#include <fcntl.h> 35#endif /* HAVE_FCNTL_H */ 36#include <ctype.h> 37#include <atalk/logger.h> 38#include <sys/param.h> 39#include <sys/socket.h> 40#include <sys/time.h> 41#ifdef HAVE_DLFCN_H 42#include <dlfcn.h> 43#endif /* HAVE_DLFCN_H */ 44 45#include <netinet/in.h> 46#include <arpa/inet.h> 47 48#include <netatalk/endian.h> 49#include <atalk/asp.h> 50#include <atalk/dsi.h> 51#include <atalk/afp.h> 52#include <atalk/util.h> 53#include <atalk/globals.h> 54 55#include "afp_config.h" 56#include "auth.h" 57#include "uam_auth.h" 58 59#define utf8_encoding() (afp_version >= 30) 60 61#ifdef TRU64 62#include <netdb.h> 63#include <sia.h> 64#include <siad.h> 65#include <signal.h> 66#endif /* TRU64 */ 67 68/* --- server uam functions -- */ 69 70/* uam_load. uams must have a uam_setup function. */ 71struct uam_mod *uam_load(const char *path, const char *name) 72{ 73 char buf[MAXPATHLEN + 1], *p; 74 struct uam_mod *mod; 75 void *module; 76 77 if ((module = mod_open(path)) == NULL) { 78 LOG(log_error, logtype_afpd, "uam_load(%s): failed to load: %s", name, mod_error()); 79 return NULL; 80 } 81 82 if ((mod = (struct uam_mod *) malloc(sizeof(struct uam_mod))) == NULL) { 83 LOG(log_error, logtype_afpd, "uam_load(%s): malloc failed", name); 84 goto uam_load_fail; 85 } 86 87 strlcpy(buf, name, sizeof(buf)); 88 if ((p = strchr(buf, '.'))) 89 *p = '\0'; 90 91 if ((mod->uam_fcn = mod_symbol(module, buf)) == NULL) { 92 LOG(log_error, logtype_afpd, "uam_load(%s): mod_symbol error for symbol %s", 93 name, 94 buf); 95 goto uam_load_err; 96 } 97 98 if (mod->uam_fcn->uam_type != UAM_MODULE_SERVER) { 99 LOG(log_error, logtype_afpd, "uam_load(%s): attempted to load a non-server module", 100 name); 101 goto uam_load_err; 102 } 103 104 /* version check would go here */ 105 106 if (!mod->uam_fcn->uam_setup || 107 ((*mod->uam_fcn->uam_setup)(name) < 0)) { 108 LOG(log_error, logtype_afpd, "uam_load(%s): uam_setup failed", name); 109 goto uam_load_err; 110 } 111 112 mod->uam_module = module; 113 return mod; 114 115uam_load_err: 116 free(mod); 117uam_load_fail: 118 mod_close(module); 119 return NULL; 120} 121 122/* unload the module. we check for a cleanup function, but we don't 123 * die if one doesn't exist. however, things are likely to leak without one. 124 */ 125void uam_unload(struct uam_mod *mod) 126{ 127 if (mod->uam_fcn->uam_cleanup) 128 (*mod->uam_fcn->uam_cleanup)(); 129 130 mod_close(mod->uam_module); 131 free(mod); 132} 133 134/* -- client-side uam functions -- */ 135/* set up stuff for this uam. */ 136int uam_register(const int type, const char *path, const char *name, ...) 137{ 138 va_list ap; 139 struct uam_obj *uam; 140 int ret; 141 142 if (!name) 143 return -1; 144 145 /* see if it already exists. */ 146 if ((uam = auth_uamfind(type, name, strlen(name)))) { 147 if (strcmp(uam->uam_path, path)) { 148 /* it exists, but it's not the same module. */ 149 LOG(log_error, logtype_afpd, "uam_register: \"%s\" already loaded by %s", 150 name, path); 151 return -1; 152 } 153 uam->uam_count++; 154 return 0; 155 } 156 157 /* allocate space for uam */ 158 if ((uam = calloc(1, sizeof(struct uam_obj))) == NULL) 159 return -1; 160 161 uam->uam_name = name; 162 uam->uam_path = strdup(path); 163 uam->uam_count++; 164 165 va_start(ap, name); 166 switch (type) { 167 case UAM_SERVER_LOGIN_EXT: /* expect four arguments */ 168 uam->u.uam_login.login = va_arg(ap, void *); 169 uam->u.uam_login.logincont = va_arg(ap, void *); 170 uam->u.uam_login.logout = va_arg(ap, void *); 171 uam->u.uam_login.login_ext = va_arg(ap, void *); 172 break; 173 174 case UAM_SERVER_LOGIN: /* expect three arguments */ 175 uam->u.uam_login.login_ext = NULL; 176 uam->u.uam_login.login = va_arg(ap, void *); 177 uam->u.uam_login.logincont = va_arg(ap, void *); 178 uam->u.uam_login.logout = va_arg(ap, void *); 179 break; 180 case UAM_SERVER_CHANGEPW: /* one argument */ 181 uam->u.uam_changepw = va_arg(ap, void *); 182 break; 183 case UAM_SERVER_PRINTAUTH: /* x arguments */ 184 default: 185 break; 186 } 187 va_end(ap); 188 189 /* attach to other uams */ 190 ret = auth_register(type, uam); 191 if ( ret) { 192 free(uam->uam_path); 193 free(uam); 194 } 195 196 return ret; 197} 198 199void uam_unregister(const int type, const char *name) 200{ 201 struct uam_obj *uam; 202 203 if (!name) 204 return; 205 206 uam = auth_uamfind(type, name, strlen(name)); 207 if (!uam || --uam->uam_count > 0) 208 return; 209 210 auth_unregister(uam); 211 free(uam->uam_path); 212 free(uam); 213} 214 215/* --- helper functions for plugin uams --- 216 * name: user name 217 * len: size of name buffer. 218*/ 219 220struct passwd *uam_getname(void *private, char *name, const int len) 221{ 222 AFPObj *obj = private; 223 struct passwd *pwent; 224 static char username[256]; 225 static char user[256]; 226 static char pwname[256]; 227 char *p; 228 size_t namelen, gecoslen = 0, pwnamelen = 0; 229 230 if ((pwent = getpwnam(name))) 231 return pwent; 232 233 /* if we have a NT domain name try with it */ 234 if (obj->options.ntdomain && obj->options.ntseparator) { 235 /* FIXME What about charset ? */ 236 size_t ulen = strlen(obj->options.ntdomain) + strlen(obj->options.ntseparator) + strlen(name); 237 if ((p = malloc(ulen +1))) { 238 strcpy(p, obj->options.ntdomain); 239 strcat(p, obj->options.ntseparator); 240 strcat(p, name); 241 pwent = getpwnam(p); 242 free(p); 243 if (pwent) { 244 int len = strlen(pwent->pw_name); 245 if (len < MAXUSERLEN) { 246 strncpy(name,pwent->pw_name, MAXUSERLEN); 247 }else{ 248 LOG(log_error, logtype_uams, "MAJOR:The name %s is longer than %d",pwent->pw_name,MAXUSERLEN); 249 } 250 251 return pwent; 252 } 253 } 254 } 255#ifndef NO_REAL_USER_NAME 256 257 if ( (size_t) -1 == (namelen = convert_string((utf8_encoding())?CH_UTF8_MAC:obj->options.maccharset, 258 CH_UCS2, name, -1, username, sizeof(username)))) 259 return NULL; 260 261 setpwent(); 262 while ((pwent = getpwent())) { 263 if ((p = strchr(pwent->pw_gecos, ','))) 264 *p = '\0'; 265 266 if ((size_t)-1 == ( gecoslen = convert_string(obj->options.unixcharset, CH_UCS2, 267 pwent->pw_gecos, -1, user, sizeof(username))) ) 268 continue; 269 if ((size_t)-1 == ( pwnamelen = convert_string(obj->options.unixcharset, CH_UCS2, 270 pwent->pw_name, -1, pwname, sizeof(username))) ) 271 continue; 272 273 274 /* check against both the gecos and the name fields. the user 275 * might have just used a different capitalization. */ 276 277 if ( (namelen == gecoslen && strncasecmp_w((ucs2_t*)user, (ucs2_t*)username, len) == 0) || 278 ( namelen == pwnamelen && strncasecmp_w ( (ucs2_t*) pwname, (ucs2_t*) username, len) == 0)) { 279 strlcpy(name, pwent->pw_name, len); 280 break; 281 } 282 } 283 endpwent(); 284#endif /* ! NO_REAL_USER_NAME */ 285 286 /* os x server doesn't keep anything useful if we do getpwent */ 287 return pwent ? getpwnam(name) : NULL; 288} 289 290int uam_checkuser(const struct passwd *pwd) 291{ 292 const char *p; 293 294 if (!pwd) 295 return -1; 296 297#ifndef DISABLE_SHELLCHECK 298 if (!pwd->pw_shell || (*pwd->pw_shell == '\0')) { 299 LOG(log_info, logtype_afpd, "uam_checkuser: User %s does not have a shell", pwd->pw_name); 300 return -1; 301 } 302 303 while ((p = getusershell())) { 304 if ( strcmp( p, pwd->pw_shell ) == 0 ) 305 break; 306 } 307 endusershell(); 308 309 if (!p) { 310 LOG(log_info, logtype_afpd, "illegal shell %s for %s", pwd->pw_shell, pwd->pw_name); 311 return -1; 312 } 313#endif /* DISABLE_SHELLCHECK */ 314 315 return 0; 316} 317 318int uam_random_string (AFPObj *obj, char *buf, int len) 319{ 320 u_int32_t result; 321 int ret; 322 int fd; 323 324 if ( (len <= 0) || (len % sizeof(result))) 325 return -1; 326 327 /* construct a random number */ 328 if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { 329 struct timeval tv; 330 struct timezone tz; 331 int i; 332 333 if (gettimeofday(&tv, &tz) < 0) 334 return -1; 335 srandom(tv.tv_sec + (unsigned long) obj + (unsigned long) obj->handle); 336 for (i = 0; i < len; i += sizeof(result)) { 337 result = random(); 338 memcpy(buf + i, &result, sizeof(result)); 339 } 340 } else { 341 ret = read(fd, buf, len); 342 close(fd); 343 if (ret <= 0) 344 return -1; 345 } 346 return 0; 347} 348 349/* afp-specific functions */ 350int uam_afpserver_option(void *private, const int what, void *option, 351 size_t *len) 352{ 353 AFPObj *obj = private; 354 const char **buf = (const char **) option; /* most of the options are this */ 355 struct session_info **sinfo = (struct session_info **) option; 356 357 if (!obj || !option) 358 return -1; 359 360 switch (what) { 361 case UAM_OPTION_USERNAME: 362 *buf = obj->username; 363 if (len) 364 *len = sizeof(obj->username) - 1; 365 break; 366 367 case UAM_OPTION_GUEST: 368 *buf = obj->options.guest; 369 if (len) 370 *len = strlen(obj->options.guest); 371 break; 372 373 case UAM_OPTION_PASSWDOPT: 374 if (!len) 375 return -1; 376 377 switch (*len) { 378 case UAM_PASSWD_FILENAME: 379 *buf = obj->options.passwdfile; 380 *len = strlen(obj->options.passwdfile); 381 break; 382 383 case UAM_PASSWD_MINLENGTH: 384 *((int *) option) = obj->options.passwdminlen; 385 *len = sizeof(obj->options.passwdminlen); 386 break; 387 388 case UAM_PASSWD_MAXFAIL: 389 *((int *) option) = obj->options.loginmaxfail; 390 *len = sizeof(obj->options.loginmaxfail); 391 break; 392 393 case UAM_PASSWD_EXPIRETIME: /* not implemented */ 394 default: 395 return -1; 396 break; 397 } 398 break; 399 400 case UAM_OPTION_SIGNATURE: 401 *buf = (void *) (((AFPConfig *)obj->config)->signature); 402 if (len) 403 *len = 16; 404 break; 405 406 case UAM_OPTION_RANDNUM: /* returns a random number in 4-byte units. */ 407 if (!len) 408 return -1; 409 410 return uam_random_string(obj, option, *len); 411 break; 412 413 case UAM_OPTION_HOSTNAME: 414 *buf = obj->options.hostname; 415 if (len) 416 *len = strlen(obj->options.hostname); 417 break; 418 419 case UAM_OPTION_PROTOCOL: 420 *((int *) option) = obj->proto; 421 break; 422 423 case UAM_OPTION_CLIENTNAME: 424 { 425 struct DSI *dsi = obj->handle; 426 const struct sockaddr *sa; 427 static char hbuf[NI_MAXHOST]; 428 429 sa = (struct sockaddr *)&dsi->client; 430 if (getnameinfo(sa, sizeof(dsi->client), hbuf, sizeof(hbuf), NULL, 0, 0) == 0) 431 *buf = hbuf; 432 else 433 *buf = getip_string((struct sockaddr *)&dsi->client); 434 435 break; 436 } 437 case UAM_OPTION_COOKIE: 438 /* it's up to the uam to actually store something useful here. 439 * this just passes back a handle to the cookie. the uam side 440 * needs to do something like **buf = (void *) cookie to store 441 * the cookie. */ 442 *buf = (void *) &obj->uam_cookie; 443 break; 444 case UAM_OPTION_KRB5SERVICE: 445 *buf = obj->options.k5service; 446 if (len) 447 *len = (*buf)?strlen(*buf):0; 448 break; 449 case UAM_OPTION_KRB5REALM: 450 *buf = obj->options.k5realm; 451 if (len) 452 *len = (*buf)?strlen(*buf):0; 453 break; 454 case UAM_OPTION_FQDN: 455 *buf = obj->options.fqdn; 456 if (len) 457 *len = (*buf)?strlen(*buf):0; 458 break; 459 case UAM_OPTION_MACCHARSET: 460 *((int *) option) = obj->options.maccharset; 461 *len = sizeof(obj->options.maccharset); 462 break; 463 case UAM_OPTION_UNIXCHARSET: 464 *((int *) option) = obj->options.unixcharset; 465 *len = sizeof(obj->options.unixcharset); 466 break; 467 case UAM_OPTION_SESSIONINFO: 468 *sinfo = &(obj->sinfo); 469 break; 470 default: 471 return -1; 472 break; 473 } 474 475 return 0; 476} 477 478/* if we need to maintain a connection, this is how we do it. 479 * because an action pointer gets passed in, we can stream 480 * DSI connections */ 481int uam_afp_read(void *handle, char *buf, size_t *buflen, 482 int (*action)(void *, void *, const int)) 483{ 484 AFPObj *obj = handle; 485 int len; 486 487 if (!obj) 488 return AFPERR_PARAM; 489 490 switch (obj->proto) { 491 case AFPPROTO_ASP: 492 if ((len = asp_wrtcont(obj->handle, buf, buflen )) < 0) 493 goto uam_afp_read_err; 494 return action(handle, buf, *buflen); 495 break; 496 497 case AFPPROTO_DSI: 498 len = dsi_writeinit(obj->handle, buf, *buflen); 499 if (!len || ((len = action(handle, buf, len)) < 0)) { 500 dsi_writeflush(obj->handle); 501 goto uam_afp_read_err; 502 } 503 504 while ((len = (dsi_write(obj->handle, buf, *buflen)))) { 505 if ((len = action(handle, buf, len)) < 0) { 506 dsi_writeflush(obj->handle); 507 goto uam_afp_read_err; 508 } 509 } 510 break; 511 } 512 return 0; 513 514uam_afp_read_err: 515 *buflen = 0; 516 return len; 517} 518 519#ifdef TRU64 520void uam_afp_getcmdline( int *ac, char ***av ) 521{ 522 afp_get_cmdline( ac, av ); 523} 524 525int uam_sia_validate_user(sia_collect_func_t * collect, int argc, char **argv, 526 char *hostname, char *username, char *tty, 527 int colinput, char *gssapi, char *passphrase) 528/* A clone of the Tru64 system function sia_validate_user() that calls 529 * sia_ses_authent() rather than sia_ses_reauthent() 530 * Added extra code to take into account suspected SIA bug whereby it clobbers 531 * the signal handler on SIGALRM (tickle) installed by Netatalk/afpd 532 */ 533{ 534 SIAENTITY *entity = NULL; 535 struct sigaction act; 536 int rc; 537 538 if ((rc=sia_ses_init(&entity, argc, argv, hostname, username, tty, 539 colinput, gssapi)) != SIASUCCESS) { 540 LOG(log_error, logtype_afpd, "cannot initialise SIA"); 541 return SIAFAIL; 542 } 543 544 /* save old action for restoration later */ 545 if (sigaction(SIGALRM, NULL, &act)) 546 LOG(log_error, logtype_afpd, "cannot save SIGALRM handler"); 547 548 if ((rc=sia_ses_authent(collect, passphrase, entity)) != SIASUCCESS) { 549 /* restore old action after clobbering by sia_ses_authent() */ 550 if (sigaction(SIGALRM, &act, NULL)) 551 LOG(log_error, logtype_afpd, "cannot restore SIGALRM handler"); 552 553 LOG(log_info, logtype_afpd, "unsuccessful login for %s", 554(hostname?hostname:"(null)")); 555 return SIAFAIL; 556 } 557 LOG(log_info, logtype_afpd, "successful login for %s", 558(hostname?hostname:"(null)")); 559 560 /* restore old action after clobbering by sia_ses_authent() */ 561 if (sigaction(SIGALRM, &act, NULL)) 562 LOG(log_error, logtype_afpd, "cannot restore SIGALRM handler"); 563 564 sia_ses_release(&entity); 565 566 return SIASUCCESS; 567} 568#endif /* TRU64 */ 569 570/* --- papd-specific functions (just placeholders) --- */ 571struct papfile; 572 573UAM_MODULE_EXPORT void append(struct papfile *pf _U_, const char *data _U_, int len _U_) 574{ 575 return; 576} 577