1/* 2 * Copyright (c) 1990,1993 Regents of The University of Michigan. 3 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 4 * All Rights Reserved. See COPYRIGHT. 5 */ 6 7#ifdef HAVE_CONFIG_H 8#include "config.h" 9#endif /* HAVE_CONFIG_H */ 10 11#if defined (USE_PAM) && defined (UAM_DHX2) 12#include <stdio.h> 13#include <stdlib.h> 14#include <string.h> 15#include <atalk/logger.h> 16 17#ifdef HAVE_UNISTD_H 18#include <unistd.h> 19#endif /* HAVE_UNISTD_H */ 20#include <errno.h> 21#ifdef HAVE_SECURITY_PAM_APPL_H 22#include <security/pam_appl.h> 23#endif 24#ifdef HAVE_PAM_PAM_APPL_H 25#include <pam/pam_appl.h> 26#endif 27 28 29#ifdef HAVE_LIBGCRYPT 30#include <gcrypt.h> 31#endif /* HAVE_LIBGCRYPT */ 32 33#include <atalk/afp.h> 34#include <atalk/uam.h> 35#include <atalk/globals.h> 36 37/* Number of bits for p which we generate. Everybode out there uses 512, so we beet them */ 38#define PRIMEBITS 1024 39 40/* hash a number to a 16-bit quantity */ 41#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \ 42 (unsigned long) (a)) & 0xffff) 43 44/* Some parameters need be maintained across calls */ 45static gcry_mpi_t p, g, Ra; 46static gcry_mpi_t serverNonce; 47static char *K_MD5hash = NULL; 48static int K_hash_len; 49static u_int16_t ID; 50 51/* The initialization vectors for CAST128 are fixed by Apple. */ 52static unsigned char dhx_c2siv[] = { 'L', 'W', 'a', 'l', 'l', 'a', 'c', 'e' }; 53static unsigned char dhx_s2civ[] = { 'C', 'J', 'a', 'l', 'b', 'e', 'r', 't' }; 54 55/* Static variables used to communicate between the conversation function 56 * and the server_login function */ 57static pam_handle_t *pamh = NULL; 58static char *PAM_username; 59static char *PAM_password; 60static struct passwd *dhxpwd; 61 62/********************************************************* 63 * Crypto helper func to generate p and g for use in DH. 64 * libgcrypt doesn't provide one directly. 65 * Algorithm taken from GNUTLS:gnutls_dh_primes.c 66 *********************************************************/ 67 68/** 69 * This function will generate a new pair of prime and generator for use in 70 * the Diffie-Hellman key exchange. 71 * The bits value should be one of 768, 1024, 2048, 3072 or 4096. 72 **/ 73static int dh_params_generate (unsigned int bits) { 74 75 int result, times = 0, qbits; 76 gcry_mpi_t *factors = NULL; 77 gcry_error_t err; 78 79 /* Version check should be the very first call because it 80 makes sure that important subsystems are intialized. */ 81 if (!gcry_check_version (GCRYPT_VERSION)) { 82 LOG(log_error, logtype_uams, "PAM DHX2: libgcrypt versions mismatch. Need: %s", GCRYPT_VERSION); 83 result = AFPERR_MISC; 84 goto error; 85 } 86 87 if (bits < 256) 88 qbits = bits / 2; 89 else 90 qbits = (bits / 40) + 105; 91 92 if (qbits & 1) /* better have an even number */ 93 qbits++; 94 95 /* find a prime number of size bits. */ 96 do { 97 if (times) { 98 gcry_mpi_release(p); 99 gcry_prime_release_factors (factors); 100 } 101 err = gcry_prime_generate(&p, bits, qbits, &factors, NULL, NULL, 102 GCRY_STRONG_RANDOM, GCRY_PRIME_FLAG_SPECIAL_FACTOR); 103 if (err != 0) { 104 result = AFPERR_MISC; 105 goto error; 106 } 107 err = gcry_prime_check(p, 0); 108 times++; 109 } while (err != 0 && times < 10); 110 111 if (err != 0) { 112 result = AFPERR_MISC; 113 goto error; 114 } 115 116 /* generate the group generator. */ 117 err = gcry_prime_group_generator(&g, p, factors, NULL); 118 if (err != 0) { 119 result = AFPERR_MISC; 120 goto error; 121 } 122 123 gcry_prime_release_factors(factors); 124 125 return 0; 126 127error: 128 gcry_prime_release_factors(factors); 129 130 return result; 131} 132 133 134/* PAM conversation function 135 * Here we assume (for now, at least) that echo on means login name, and 136 * echo off means password. 137 */ 138static int PAM_conv (int num_msg, 139 const struct pam_message **msg, 140 struct pam_response **resp, 141 void *appdata_ptr _U_) { 142 int count = 0; 143 struct pam_response *reply; 144 145#define COPY_STRING(s) (s) ? strdup(s) : NULL 146 147 errno = 0; 148 149 if (num_msg < 1) { 150 /* Log Entry */ 151 LOG(log_info, logtype_uams, "PAM DHX2 Conversation Err -- %s", 152 strerror(errno)); 153 /* Log Entry */ 154 return PAM_CONV_ERR; 155 } 156 157 reply = (struct pam_response *) 158 calloc(num_msg, sizeof(struct pam_response)); 159 160 if (!reply) { 161 /* Log Entry */ 162 LOG(log_info, logtype_uams, "PAM DHX2: Conversation Err -- %s", 163 strerror(errno)); 164 /* Log Entry */ 165 return PAM_CONV_ERR; 166 } 167 168 for (count = 0; count < num_msg; count++) { 169 char *string = NULL; 170 171 switch (msg[count]->msg_style) { 172 case PAM_PROMPT_ECHO_ON: 173 if (!(string = COPY_STRING(PAM_username))) { 174 /* Log Entry */ 175 LOG(log_info, logtype_uams, "PAM DHX2: username failure -- %s", 176 strerror(errno)); 177 /* Log Entry */ 178 goto pam_fail_conv; 179 } 180 break; 181 case PAM_PROMPT_ECHO_OFF: 182 if (!(string = COPY_STRING(PAM_password))) { 183 /* Log Entry */ 184 LOG(log_info, logtype_uams, "PAM DHX2: passwd failure: --: %s", 185 strerror(errno)); 186 /* Log Entry */ 187 goto pam_fail_conv; 188 } 189 break; 190 case PAM_TEXT_INFO: 191#ifdef PAM_BINARY_PROMPT 192 case PAM_BINARY_PROMPT: 193#endif /* PAM_BINARY_PROMPT */ 194 /* ignore it... */ 195 break; 196 case PAM_ERROR_MSG: 197 default: 198 LOG(log_info, logtype_uams, "PAM DHX2: Binary_Prompt -- %s", strerror(errno)); 199 goto pam_fail_conv; 200 } 201 202 if (string) { 203 reply[count].resp_retcode = 0; 204 reply[count].resp = string; 205 string = NULL; 206 } 207 } 208 209 *resp = reply; 210 LOG(log_info, logtype_uams, "PAM DHX2: PAM Success"); 211 return PAM_SUCCESS; 212 213pam_fail_conv: 214 for (count = 0; count < num_msg; count++) { 215 if (!reply[count].resp) 216 continue; 217 switch (msg[count]->msg_style) { 218 case PAM_PROMPT_ECHO_OFF: 219 case PAM_PROMPT_ECHO_ON: 220 free(reply[count].resp); 221 break; 222 } 223 } 224 free(reply); 225 /* Log Entry */ 226 LOG(log_info, logtype_uams, "PAM DHX2: Conversation Err -- %s", 227 strerror(errno)); 228 /* Log Entry */ 229 return PAM_CONV_ERR; 230} 231 232static struct pam_conv PAM_conversation = { 233 &PAM_conv, 234 NULL 235}; 236 237 238static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_, 239 char *rbuf, size_t *rbuflen) 240{ 241 int ret; 242 size_t nwritten; 243 gcry_mpi_t Ma; 244 char *Ra_binary = NULL; 245 246 *rbuflen = 0; 247 248 Ra = gcry_mpi_new(0); 249 Ma = gcry_mpi_new(0); 250 251 /* Generate our random number Ra. */ 252 Ra_binary = calloc(1, PRIMEBITS/8); 253 if (Ra_binary == NULL) { 254 ret = AFPERR_MISC; 255 goto error; 256 } 257 gcry_randomize(Ra_binary, PRIMEBITS/8, GCRY_STRONG_RANDOM); 258 gcry_mpi_scan(&Ra, GCRYMPI_FMT_USG, Ra_binary, PRIMEBITS/8, NULL); 259 free(Ra_binary); 260 Ra_binary = NULL; 261 262 /* Ma = g^Ra mod p. This is our "public" key */ 263 gcry_mpi_powm(Ma, g, Ra, p); 264 265 /* ------- DH Init done ------ */ 266 /* Start building reply packet */ 267 268 /* Session ID first */ 269 ID = dhxhash(obj); 270 *(u_int16_t *)rbuf = htons(ID); 271 rbuf += 2; 272 *rbuflen += 2; 273 274 /* g is next */ 275 gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, 4, &nwritten, g); 276 if (nwritten < 4) { 277 memmove( rbuf+4-nwritten, rbuf, nwritten); 278 memset( rbuf, 0, 4-nwritten); 279 } 280 rbuf += 4; 281 *rbuflen += 4; 282 283 /* len = length of p = PRIMEBITS/8 */ 284 *(u_int16_t *)rbuf = htons((u_int16_t) PRIMEBITS/8); 285 rbuf += 2; 286 *rbuflen += 2; 287 288 /* p */ 289 gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, NULL, p); 290 rbuf += PRIMEBITS/8; 291 *rbuflen += PRIMEBITS/8; 292 293 /* Ma */ 294 gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, &nwritten, Ma); 295 if (nwritten < PRIMEBITS/8) { 296 memmove(rbuf + (PRIMEBITS/8) - nwritten, rbuf, nwritten); 297 memset(rbuf, 0, (PRIMEBITS/8) - nwritten); 298 } 299 rbuf += PRIMEBITS/8; 300 *rbuflen += PRIMEBITS/8; 301 302 ret = AFPERR_AUTHCONT; 303 304error: /* We exit here anyway */ 305 /* We will need Ra later, but mustn't forget to release it ! */ 306 gcry_mpi_release(Ma); 307 return ret; 308} 309 310/* -------------------------------- */ 311static int login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_, 312 char *ibuf, size_t ibuflen, 313 char *rbuf, size_t *rbuflen) 314{ 315 if (( dhxpwd = uam_getname(obj, username, ulen)) == NULL ) { 316 LOG(log_info, logtype_uams, "DHX2: unknown username"); 317 return AFPERR_NOTAUTH; 318 } 319 320 PAM_username = username; 321 LOG(log_info, logtype_uams, "DHX2 login: %s", username); 322 return dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen); 323} 324 325/* -------------------------------- */ 326/* dhx login: things are done in a slightly bizarre order to avoid 327 * having to clean things up if there's an error. */ 328static int pam_login(void *obj, struct passwd **uam_pwd, 329 char *ibuf, size_t ibuflen, 330 char *rbuf, size_t *rbuflen) 331{ 332 char *username; 333 size_t len, ulen; 334 335 *rbuflen = 0; 336 337 /* grab some of the options */ 338 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) { 339 LOG(log_info, logtype_uams, "DHX2: uam_afpserver_option didn't meet uam_option_username -- %s", 340 strerror(errno)); 341 return AFPERR_PARAM; 342 } 343 344 len = (unsigned char) *ibuf++; 345 if ( len > ulen ) { 346 LOG(log_info, logtype_uams, "DHX2: Signature Retieval Failure -- %s", 347 strerror(errno)); 348 return AFPERR_PARAM; 349 } 350 351 memcpy(username, ibuf, len ); 352 ibuf += len; 353 username[ len ] = '\0'; 354 355 if ((unsigned long) ibuf & 1) /* pad to even boundary */ 356 ++ibuf; 357 358 return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen)); 359} 360 361/* ----------------------------- */ 362static int pam_login_ext(void *obj, char *uname, struct passwd **uam_pwd, 363 char *ibuf, size_t ibuflen, 364 char *rbuf, size_t *rbuflen) 365{ 366 char *username; 367 size_t len, ulen; 368 u_int16_t temp16; 369 370 *rbuflen = 0; 371 372 /* grab some of the options */ 373 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) { 374 LOG(log_info, logtype_uams, "DHX2: uam_afpserver_option didn't meet uam_option_username -- %s", 375 strerror(errno)); 376 return AFPERR_PARAM; 377 } 378 379 if (*uname != 3) 380 return AFPERR_PARAM; 381 uname++; 382 memcpy(&temp16, uname, sizeof(temp16)); 383 len = ntohs(temp16); 384 385 if ( !len || len > ulen ) { 386 LOG(log_info, logtype_uams, "DHX2: Signature Retrieval Failure -- %s", 387 strerror(errno)); 388 return AFPERR_PARAM; 389 } 390 memcpy(username, uname +2, len ); 391 username[ len ] = '\0'; 392 393 return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen)); 394} 395 396/* -------------------------------- */ 397static int logincont1(void *obj _U_, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen) 398{ 399 int ret; 400 size_t nwritten; 401 gcry_mpi_t Mb, K, clientNonce; 402 unsigned char *K_bin = NULL; 403 char serverNonce_bin[16]; 404 gcry_cipher_hd_t ctx; 405 gcry_error_t ctxerror; 406 407 *rbuflen = 0; 408 409 Mb = gcry_mpi_new(0); 410 K = gcry_mpi_new(0); 411 clientNonce = gcry_mpi_new(0); 412 serverNonce = gcry_mpi_new(0); 413 414 /* Packet size should be: Session ID + Ma + Encrypted client nonce */ 415 if (ibuflen != 2 + PRIMEBITS/8 + 16) { 416 LOG(log_error, logtype_uams, "DHX2: Paket length not correct"); 417 ret = AFPERR_PARAM; 418 goto error_noctx; 419 } 420 421 /* Skip session id */ 422 ibuf += 2; 423 424 /* Extract Mb, client's "public" key */ 425 gcry_mpi_scan(&Mb, GCRYMPI_FMT_USG, ibuf, PRIMEBITS/8, NULL); 426 ibuf += PRIMEBITS/8; 427 428 /* Now finally generate the Key: K = Mb^Ra mod p */ 429 gcry_mpi_powm(K, Mb, Ra, p); 430 431 /* We need K in binary form in order to ... */ 432 K_bin = calloc(1, PRIMEBITS/8); 433 if (K_bin == NULL) { 434 ret = AFPERR_MISC; 435 goto error_noctx; 436 } 437 gcry_mpi_print(GCRYMPI_FMT_USG, K_bin, PRIMEBITS/8, &nwritten, K); 438 if (nwritten < PRIMEBITS/8) { 439 memmove(K_bin + PRIMEBITS/8 - nwritten, K_bin, nwritten); 440 memset(K_bin, 0, PRIMEBITS/8 - nwritten); 441 } 442 443 /* ... generate the MD5 hash of K. K_MD5hash is what we actually use ! */ 444 K_MD5hash = calloc(1, K_hash_len = gcry_md_get_algo_dlen(GCRY_MD_MD5)); 445 if (K_MD5hash == NULL) { 446 ret = AFPERR_MISC; 447 goto error_noctx; 448 } 449 gcry_md_hash_buffer(GCRY_MD_MD5, K_MD5hash, K_bin, PRIMEBITS/8); 450 free(K_bin); 451 K_bin = NULL; 452 453 /* FIXME: To support the Reconnect UAM, we need to store this key somewhere */ 454 455 /* Set up our encryption context. */ 456 ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0); 457 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 458 ret = AFPERR_MISC; 459 goto error_ctx; 460 } 461 /* Set key */ 462 ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len); 463 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 464 ret = AFPERR_MISC; 465 goto error_ctx; 466 } 467 /* Set the initialization vector for client->server transfer. */ 468 ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv)); 469 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 470 ret = AFPERR_MISC; 471 goto error_ctx; 472 } 473 /* Finally: decrypt client's md5_K(client nonce, C2SIV) inplace */ 474 ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16, NULL, 0); 475 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 476 ret = AFPERR_MISC; 477 goto error_ctx; 478 } 479 /* Pull out clients nonce */ 480 gcry_mpi_scan(&clientNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL); 481 /* Increment nonce */ 482 gcry_mpi_add_ui(clientNonce, clientNonce, 1); 483 484 /* Generate our nonce and remember it for Logincont2 */ 485 gcry_create_nonce(serverNonce_bin, 16); /* We'll use this here */ 486 gcry_mpi_scan(&serverNonce, GCRYMPI_FMT_USG, serverNonce_bin, 16, NULL); /* For use in Logincont2 */ 487 488 /* ---- Start building reply packet ---- */ 489 490 /* Session ID + 1 first */ 491 *(u_int16_t *)rbuf = htons(ID+1); 492 rbuf += 2; 493 *rbuflen += 2; 494 495 /* Client nonce + 1 */ 496 gcry_mpi_print(GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, NULL, clientNonce); 497 /* Server nonce */ 498 memcpy(rbuf+16, serverNonce_bin, 16); 499 500 /* Set the initialization vector for server->client transfer. */ 501 ctxerror = gcry_cipher_setiv(ctx, dhx_s2civ, sizeof(dhx_s2civ)); 502 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 503 ret = AFPERR_MISC; 504 goto error_ctx; 505 } 506 /* Encrypt md5_K(clientNonce+1, serverNonce) inplace */ 507 ctxerror = gcry_cipher_encrypt(ctx, rbuf, 32, NULL, 0); 508 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 509 ret = AFPERR_MISC; 510 goto error_ctx; 511 } 512 rbuf += 32; 513 *rbuflen += 32; 514 515 ret = AFPERR_AUTHCONT; 516 goto exit; 517 518error_ctx: 519 gcry_cipher_close(ctx); 520error_noctx: 521 gcry_mpi_release(serverNonce); 522 free(K_MD5hash); 523 K_MD5hash=NULL; 524exit: 525 gcry_mpi_release(K); 526 gcry_mpi_release(Mb); 527 gcry_mpi_release(Ra); 528 gcry_mpi_release(clientNonce); 529 return ret; 530} 531 532/** 533 * Try to authenticate via PAM as "adminauthuser" 534 **/ 535static int loginasroot(const char *adminauthuser, const char **hostname, int status) 536{ 537 int PAM_error; 538 539 if ((PAM_error = pam_end(pamh, status)) != PAM_SUCCESS) 540 goto exit; 541 pamh = NULL; 542 543 if ((PAM_error = pam_start("netatalk", adminauthuser, &PAM_conversation, &pamh)) != PAM_SUCCESS) { 544 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s", pam_strerror(pamh,PAM_error)); 545 goto exit; 546 } 547 548 /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */ 549 pam_set_item(pamh, PAM_TTY, "afpd"); 550 pam_set_item(pamh, PAM_RHOST, *hostname); 551 if ((PAM_error = pam_authenticate(pamh, 0)) != PAM_SUCCESS) 552 goto exit; 553 554 LOG(log_warning, logtype_uams, "DHX2: Authenticated as \"%s\"", adminauthuser); 555 556exit: 557 return PAM_error; 558} 559 560static int logincont2(void *obj_in, struct passwd **uam_pwd, 561 char *ibuf, size_t ibuflen, 562 char *rbuf _U_, size_t *rbuflen) 563{ 564 AFPObj *obj = obj_in; 565 int ret = AFPERR_MISC; 566 int PAM_error; 567 const char *hostname = NULL; 568 gcry_mpi_t retServerNonce; 569 gcry_cipher_hd_t ctx; 570 gcry_error_t ctxerror; 571 char *utfpass = NULL; 572 573 *rbuflen = 0; 574 575 /* Packet size should be: Session ID + ServerNonce + Passwd buffer (evantually +10 extra bytes, see Apples Docs) */ 576 if ((ibuflen != 2 + 16 + 256) && (ibuflen != 2 + 16 + 256 + 10)) { 577 LOG(log_error, logtype_uams, "DHX2: Paket length not correct: %u. Should be 274 or 284.", ibuflen); 578 ret = AFPERR_PARAM; 579 goto error_noctx; 580 } 581 582 retServerNonce = gcry_mpi_new(0); 583 584 /* For PAM */ 585 uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL); 586 587 /* Set up our encryption context. */ 588 ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0); 589 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 590 ret = AFPERR_MISC; 591 goto error_ctx; 592 } 593 /* Set key */ 594 ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len); 595 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 596 ret = AFPERR_MISC; 597 goto error_ctx; 598 } 599 /* Set the initialization vector for client->server transfer. */ 600 ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv)); 601 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 602 ret = AFPERR_MISC; 603 goto error_ctx; 604 } 605 606 /* Skip Session ID */ 607 ibuf += 2; 608 609 /* Finally: decrypt client's md5_K(serverNonce+1, passwor, C2SIV) inplace */ 610 ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16+256, NULL, 0); 611 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 612 ret = AFPERR_MISC; 613 goto error_ctx; 614 } 615 /* Pull out nonce. Should be serverNonce+1 */ 616 gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL); 617 gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1); 618 if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) { 619 /* We're hacked! */ 620 ret = AFPERR_NOTAUTH; 621 goto error_ctx; 622 } 623 ibuf += 16; 624 625 /* ---- Start authentication with PAM --- */ 626 627 /* The password is in legacy Mac encoding, convert it to host encoding */ 628 if (convert_string_allocate(CH_MAC, CH_UNIX, ibuf, -1, &utfpass) == (size_t)-1) { 629 LOG(log_error, logtype_uams, "DHX2: conversion error"); 630 goto error_ctx; 631 } 632 PAM_password = utfpass; 633 634#ifdef DEBUG 635 LOG(log_maxdebug, logtype_default, "DHX2: password: %s", PAM_password); 636#endif 637 638 /* Set these things up for the conv function */ 639 640 ret = AFPERR_NOTAUTH; 641 PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation, &pamh); 642 if (PAM_error != PAM_SUCCESS) { 643 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s", pam_strerror(pamh,PAM_error)); 644 goto error_ctx; 645 } 646 647 /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */ 648 pam_set_item(pamh, PAM_TTY, "afpd"); 649 pam_set_item(pamh, PAM_RHOST, hostname); 650 pam_set_item(pamh, PAM_RUSER, PAM_username); 651 652 PAM_error = pam_authenticate(pamh, 0); 653 if (PAM_error != PAM_SUCCESS) { 654 if (PAM_error == PAM_MAXTRIES) 655 ret = AFPERR_PWDEXPR; 656 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s", pam_strerror(pamh, PAM_error)); 657 658 if (!obj->options.adminauthuser) 659 goto error_ctx; 660 if (loginasroot(obj->options.adminauthuser, &hostname, PAM_error) != PAM_SUCCESS) { 661 goto error_ctx; 662 } 663 } 664 665 PAM_error = pam_acct_mgmt(pamh, 0); 666 if (PAM_error != PAM_SUCCESS ) { 667 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s", 668 pam_strerror(pamh, PAM_error)); 669 if (PAM_error == PAM_NEW_AUTHTOK_REQD) /* password expired */ 670 ret = AFPERR_PWDEXPR; 671#ifdef PAM_AUTHTOKEN_REQD 672 else if (PAM_error == PAM_AUTHTOKEN_REQD) 673 ret = AFPERR_PWDCHNG; 674#endif 675 goto error_ctx; 676 } 677 678#ifndef PAM_CRED_ESTABLISH 679#define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED 680#endif 681 PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH); 682 if (PAM_error != PAM_SUCCESS) { 683 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s", 684 pam_strerror(pamh, PAM_error)); 685 goto error_ctx; 686 } 687 688 PAM_error = pam_open_session(pamh, 0); 689 if (PAM_error != PAM_SUCCESS) { 690 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s", 691 pam_strerror(pamh, PAM_error)); 692 goto error_ctx; 693 } 694 695 memset(ibuf, 0, 256); /* zero out the password */ 696 if (utfpass) 697 memset(utfpass, 0, strlen(utfpass)); 698 *uam_pwd = dhxpwd; 699 LOG(log_info, logtype_uams, "DHX2: PAM Auth OK!"); 700 701 ret = AFP_OK; 702 703error_ctx: 704 gcry_cipher_close(ctx); 705error_noctx: 706 if (utfpass) free(utfpass); 707 free(K_MD5hash); 708 K_MD5hash=NULL; 709 gcry_mpi_release(serverNonce); 710 gcry_mpi_release(retServerNonce); 711 return ret; 712} 713 714static int pam_logincont(void *obj, struct passwd **uam_pwd, 715 char *ibuf, size_t ibuflen, 716 char *rbuf, size_t *rbuflen) 717{ 718 u_int16_t retID; 719 int ret; 720 721 /* check for session id */ 722 retID = ntohs(*(u_int16_t *)ibuf); 723 if (retID == ID) 724 ret = logincont1(obj, ibuf, ibuflen, rbuf, rbuflen); 725 else if (retID == ID+1) 726 ret = logincont2(obj, uam_pwd, ibuf,ibuflen, rbuf, rbuflen); 727 else { 728 LOG(log_info, logtype_uams, "DHX2: Session ID Mismatch"); 729 ret = AFPERR_PARAM; 730 } 731 return ret; 732} 733 734 735/* logout */ 736static void pam_logout(void) { 737 pam_close_session(pamh, 0); 738 pam_end(pamh, 0); 739 pamh = NULL; 740} 741 742/**************************** 743 * --- Change pwd stuff --- */ 744 745static int changepw_1(void *obj, char *uname, 746 char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen) 747{ 748 *rbuflen = 0; 749 750 /* Remember it now, use it in changepw_3 */ 751 PAM_username = uname; 752 return( dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen) ); 753} 754 755static int changepw_2(void *obj, 756 char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen) 757{ 758 return( logincont1(obj, ibuf, ibuflen, rbuf, rbuflen) ); 759} 760 761static int changepw_3(void *obj _U_, 762 char *ibuf, size_t ibuflen _U_, 763 char *rbuf _U_, size_t *rbuflen _U_) 764{ 765 int ret; 766 int PAM_error; 767 uid_t uid; 768 pam_handle_t *lpamh; 769 const char *hostname = NULL; 770 gcry_mpi_t retServerNonce; 771 gcry_cipher_hd_t ctx; 772 gcry_error_t ctxerror; 773 774 *rbuflen = 0; 775 776 LOG(log_error, logtype_uams, "DHX2 ChangePW: packet 3 processing"); 777 778 /* Packet size should be: Session ID + ServerNonce + 2*Passwd buffer */ 779 if (ibuflen != 2 + 16 + 2*256) { 780 LOG(log_error, logtype_uams, "DHX2: Paket length not correct"); 781 ret = AFPERR_PARAM; 782 goto error_noctx; 783 } 784 785 retServerNonce = gcry_mpi_new(0); 786 787 /* For PAM */ 788 uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL); 789 790 /* Set up our encryption context. */ 791 ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0); 792 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 793 ret = AFPERR_MISC; 794 goto error_ctx; 795 } 796 /* Set key */ 797 ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len); 798 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 799 ret = AFPERR_MISC; 800 goto error_ctx; 801 } 802 803 /* Set the initialization vector for client->server transfer. */ 804 ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv)); 805 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 806 ret = AFPERR_MISC; 807 goto error_ctx; 808 } 809 810 /* Skip Session ID */ 811 ibuf += 2; 812 813 /* Finally: decrypt client's md5_K(serverNonce+1, 2*password, C2SIV) inplace */ 814 ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16+2*256, NULL, 0); 815 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) { 816 ret = AFPERR_MISC; 817 goto error_ctx; 818 } 819 /* Pull out nonce. Should be serverNonce+1 */ 820 gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL); 821 gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1); 822 if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) { 823 /* We're hacked! */ 824 ret = AFPERR_NOTAUTH; 825 goto error_ctx; 826 } 827 ibuf += 16; 828 829 /* ---- Start pwd changing with PAM --- */ 830 ibuf[255] = '\0'; /* For safety */ 831 ibuf[511] = '\0'; 832 833 /* check if new and old password are equal */ 834 if (memcmp(ibuf, ibuf + 256, 255) == 0) { 835 LOG(log_info, logtype_uams, "DHX2 Chgpwd: new and old password are equal"); 836 ret = AFPERR_PWDSAME; 837 goto error_ctx; 838 } 839 840 /* Set these things up for the conv function. PAM_username was set in changepw_1 */ 841 PAM_password = ibuf + 256; 842 PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation, &lpamh); 843 if (PAM_error != PAM_SUCCESS) { 844 LOG(log_info, logtype_uams, "DHX2 Chgpwd: PAM error in pam_start"); 845 ret = AFPERR_PARAM; 846 goto error_ctx; 847 } 848 pam_set_item(lpamh, PAM_TTY, "afpd"); 849 uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL); 850 pam_set_item(lpamh, PAM_RHOST, hostname); 851 uid = geteuid(); 852 seteuid(0); 853 PAM_error = pam_authenticate(lpamh,0); 854 if (PAM_error != PAM_SUCCESS) { 855 LOG(log_info, logtype_uams, "DHX2 Chgpwd: error authenticating with PAM"); 856 seteuid(uid); 857 pam_end(lpamh, PAM_error); 858 ret = AFPERR_NOTAUTH; 859 goto error_ctx; 860 } 861 PAM_password = ibuf; 862 PAM_error = pam_chauthtok(lpamh, 0); 863 seteuid(uid); /* un-root ourselves. */ 864 memset(ibuf, 0, 512); 865 if (PAM_error != PAM_SUCCESS) { 866 LOG(log_info, logtype_uams, "DHX2 Chgpwd: error changing pw with PAM"); 867 pam_end(lpamh, PAM_error); 868 ret = AFPERR_ACCESS; 869 goto error_ctx; 870 } 871 pam_end(lpamh, 0); 872 ret = AFP_OK; 873 874error_ctx: 875 gcry_cipher_close(ctx); 876error_noctx: 877 free(K_MD5hash); 878 K_MD5hash=NULL; 879 gcry_mpi_release(serverNonce); 880 gcry_mpi_release(retServerNonce); 881 return ret; 882} 883 884static int dhx2_changepw(void *obj _U_, char *uname, 885 struct passwd *pwd _U_, char *ibuf, size_t ibuflen _U_, 886 char *rbuf _U_, size_t *rbuflen _U_) 887{ 888 /* We use this to serialize the three incoming FPChangePassword calls */ 889 static int dhx2_changepw_status = 1; 890 891 int ret = AFPERR_NOTAUTH; /* gcc can't figure out it's always initialized */ 892 893 switch (dhx2_changepw_status) { 894 case 1: 895 ret = changepw_1( obj, uname, ibuf, ibuflen, rbuf, rbuflen); 896 if ( ret == AFPERR_AUTHCONT) 897 dhx2_changepw_status = 2; 898 break; 899 case 2: 900 ret = changepw_2( obj, ibuf, ibuflen, rbuf, rbuflen); 901 if ( ret == AFPERR_AUTHCONT) 902 dhx2_changepw_status = 3; 903 else 904 dhx2_changepw_status = 1; 905 break; 906 case 3: 907 ret = changepw_3( obj, ibuf, ibuflen, rbuf, rbuflen); 908 dhx2_changepw_status = 1; /* Whether is was succesfull or not: we 909 restart anyway !*/ 910 break; 911 } 912 return ret; 913} 914 915static int uam_setup(const char *path) 916{ 917 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHX2", pam_login, 918 pam_logincont, pam_logout, pam_login_ext) < 0) 919 return -1; 920 if (uam_register(UAM_SERVER_CHANGEPW, path, "DHX2", dhx2_changepw) < 0) 921 return -1; 922 923 p = gcry_mpi_new(0); 924 g = gcry_mpi_new(0); 925 926 LOG(log_debug, logtype_uams, "DHX2: generating mersenne primes"); 927 /* Generate p and g for DH */ 928 if (dh_params_generate(PRIMEBITS) != 0) { 929 LOG(log_error, logtype_uams, "DHX2: Couldn't generate p and g"); 930 return -1; 931 } 932 933 return 0; 934} 935 936static void uam_cleanup(void) 937{ 938 uam_unregister(UAM_SERVER_LOGIN, "DHX2"); 939 uam_unregister(UAM_SERVER_CHANGEPW, "DHX2"); 940 941 gcry_mpi_release(p); 942 gcry_mpi_release(g); 943} 944 945 946UAM_MODULE_EXPORT struct uam_export uams_dhx2 = { 947 UAM_MODULE_SERVER, 948 UAM_MODULE_VERSION, 949 uam_setup, uam_cleanup 950}; 951 952 953UAM_MODULE_EXPORT struct uam_export uams_dhx2_pam = { 954 UAM_MODULE_SERVER, 955 UAM_MODULE_VERSION, 956 uam_setup, uam_cleanup 957}; 958 959#endif /* USE_PAM && UAM_DHX2 */ 960