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.
9 #endif /* HAVE_CONFIG_H */
11 #if defined (USE_PAM) && defined (UAM_DHX2)
15 #include <atalk/logger.h>
19 #endif /* HAVE_UNISTD_H */
21 #ifdef HAVE_SECURITY_PAM_APPL_H
22 #include <security/pam_appl.h>
24 #ifdef HAVE_PAM_PAM_APPL_H
25 #include <pam/pam_appl.h>
31 #endif /* HAVE_LIBGCRYPT */
33 #include <atalk/afp.h>
34 #include <atalk/uam.h>
35 #include <atalk/globals.h>
37 /* Number of bits for p which we generate. Everybode out there uses 512, so we beet them */
38 #define PRIMEBITS 1024
40 /* hash a number to a 16-bit quantity */
41 #define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
42 (unsigned long) (a)) & 0xffff)
44 /* Some parameters need be maintained across calls */
45 static gcry_mpi_t p, g, Ra;
46 static gcry_mpi_t serverNonce;
47 static char *K_MD5hash = NULL;
48 static int K_hash_len;
51 /* The initialization vectors for CAST128 are fixed by Apple. */
52 static unsigned char dhx_c2siv[] = { 'L', 'W', 'a', 'l', 'l', 'a', 'c', 'e' };
53 static unsigned char dhx_s2civ[] = { 'C', 'J', 'a', 'l', 'b', 'e', 'r', 't' };
55 /* Static variables used to communicate between the conversation function
56 * and the server_login function */
57 static pam_handle_t *pamh = NULL;
58 static char *PAM_username;
59 static char *PAM_password;
60 static struct passwd *dhxpwd;
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 *********************************************************/
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.
73 static int dh_params_generate (unsigned int bits) {
75 int result, times = 0, qbits;
76 gcry_mpi_t *factors = NULL;
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);
90 qbits = (bits / 40) + 105;
92 if (qbits & 1) /* better have an even number */
95 /* find a prime number of size bits. */
99 gcry_prime_release_factors (factors);
101 err = gcry_prime_generate(&p, bits, qbits, &factors, NULL, NULL,
102 GCRY_STRONG_RANDOM, GCRY_PRIME_FLAG_SPECIAL_FACTOR);
104 result = AFPERR_MISC;
107 err = gcry_prime_check(p, 0);
109 } while (err != 0 && times < 10);
112 result = AFPERR_MISC;
116 /* generate the group generator. */
117 err = gcry_prime_group_generator(&g, p, factors, NULL);
119 result = AFPERR_MISC;
123 gcry_prime_release_factors(factors);
128 gcry_prime_release_factors(factors);
134 /* PAM conversation function
135 * Here we assume (for now, at least) that echo on means login name, and
136 * echo off means password.
138 static int PAM_conv (int num_msg,
140 const struct pam_message **msg,
142 struct pam_message **msg,
144 struct pam_response **resp,
145 void *appdata_ptr _U_) {
147 struct pam_response *reply;
149 #define COPY_STRING(s) (s) ? strdup(s) : NULL
155 LOG(log_info, logtype_uams, "PAM DHX2 Conversation Err -- %s",
161 reply = (struct pam_response *)
162 calloc(num_msg, sizeof(struct pam_response));
166 LOG(log_info, logtype_uams, "PAM DHX2: Conversation Err -- %s",
172 for (count = 0; count < num_msg; count++) {
175 switch (msg[count]->msg_style) {
176 case PAM_PROMPT_ECHO_ON:
177 if (!(string = COPY_STRING(PAM_username))) {
179 LOG(log_info, logtype_uams, "PAM DHX2: username failure -- %s",
185 case PAM_PROMPT_ECHO_OFF:
186 if (!(string = COPY_STRING(PAM_password))) {
188 LOG(log_info, logtype_uams, "PAM DHX2: passwd failure: --: %s",
195 #ifdef PAM_BINARY_PROMPT
196 case PAM_BINARY_PROMPT:
197 #endif /* PAM_BINARY_PROMPT */
202 LOG(log_info, logtype_uams, "PAM DHX2: Binary_Prompt -- %s", strerror(errno));
207 reply[count].resp_retcode = 0;
208 reply[count].resp = string;
214 LOG(log_info, logtype_uams, "PAM DHX2: PAM Success");
218 for (count = 0; count < num_msg; count++) {
219 if (!reply[count].resp)
221 switch (msg[count]->msg_style) {
222 case PAM_PROMPT_ECHO_OFF:
223 case PAM_PROMPT_ECHO_ON:
224 free(reply[count].resp);
230 LOG(log_info, logtype_uams, "PAM DHX2: Conversation Err -- %s",
236 static struct pam_conv PAM_conversation = {
242 static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_,
243 char *rbuf, size_t *rbuflen)
248 char *Ra_binary = NULL;
253 Ra = gcry_mpi_new(0);
254 Ma = gcry_mpi_new(0);
256 /* Generate our random number Ra. */
257 Ra_binary = calloc(1, PRIMEBITS/8);
258 if (Ra_binary == NULL) {
262 gcry_randomize(Ra_binary, PRIMEBITS/8, GCRY_STRONG_RANDOM);
263 gcry_mpi_scan(&Ra, GCRYMPI_FMT_USG, Ra_binary, PRIMEBITS/8, NULL);
267 /* Ma = g^Ra mod p. This is our "public" key */
268 gcry_mpi_powm(Ma, g, Ra, p);
270 /* ------- DH Init done ------ */
271 /* Start building reply packet */
273 /* Session ID first */
276 memcpy(rbuf, &uint16, sizeof(uint16_t));
281 gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, 4, &nwritten, g);
283 memmove( rbuf+4-nwritten, rbuf, nwritten);
284 memset( rbuf, 0, 4-nwritten);
289 /* len = length of p = PRIMEBITS/8 */
291 uint16 = htons((uint16_t) PRIMEBITS/8);
292 memcpy(rbuf, &uint16, sizeof(uint16_t));
297 gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, NULL, p);
299 *rbuflen += PRIMEBITS/8;
302 gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, &nwritten, Ma);
303 if (nwritten < PRIMEBITS/8) {
304 memmove(rbuf + (PRIMEBITS/8) - nwritten, rbuf, nwritten);
305 memset(rbuf, 0, (PRIMEBITS/8) - nwritten);
308 *rbuflen += PRIMEBITS/8;
310 ret = AFPERR_AUTHCONT;
312 error: /* We exit here anyway */
313 /* We will need Ra later, but mustn't forget to release it ! */
314 gcry_mpi_release(Ma);
318 /* -------------------------------- */
319 static int login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_,
320 char *ibuf, size_t ibuflen,
321 char *rbuf, size_t *rbuflen)
323 if (( dhxpwd = uam_getname(obj, username, ulen)) == NULL ) {
324 LOG(log_info, logtype_uams, "DHX2: unknown username");
325 return AFPERR_NOTAUTH;
328 PAM_username = username;
329 LOG(log_info, logtype_uams, "DHX2 login: %s", username);
330 return dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
333 /* -------------------------------- */
334 /* dhx login: things are done in a slightly bizarre order to avoid
335 * having to clean things up if there's an error. */
336 static int pam_login(void *obj, struct passwd **uam_pwd,
337 char *ibuf, size_t ibuflen,
338 char *rbuf, size_t *rbuflen)
345 /* grab some of the options */
346 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
347 LOG(log_info, logtype_uams, "DHX2: uam_afpserver_option didn't meet uam_option_username -- %s",
352 len = (unsigned char) *ibuf++;
354 LOG(log_info, logtype_uams, "DHX2: Signature Retieval Failure -- %s",
359 memcpy(username, ibuf, len );
361 username[ len ] = '\0';
363 if ((unsigned long) ibuf & 1) /* pad to even boundary */
366 return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
369 /* ----------------------------- */
370 static int pam_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
371 char *ibuf, size_t ibuflen,
372 char *rbuf, size_t *rbuflen)
380 /* grab some of the options */
381 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
382 LOG(log_info, logtype_uams, "DHX2: uam_afpserver_option didn't meet uam_option_username -- %s",
390 memcpy(&temp16, uname, sizeof(temp16));
393 if ( !len || len > ulen ) {
394 LOG(log_info, logtype_uams, "DHX2: Signature Retrieval Failure -- %s",
398 memcpy(username, uname +2, len );
399 username[ len ] = '\0';
401 return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
404 /* -------------------------------- */
405 static int logincont1(void *obj _U_, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
409 gcry_mpi_t Mb, K, clientNonce;
410 unsigned char *K_bin = NULL;
411 char serverNonce_bin[16];
412 gcry_cipher_hd_t ctx;
413 gcry_error_t ctxerror;
418 Mb = gcry_mpi_new(0);
420 clientNonce = gcry_mpi_new(0);
421 serverNonce = gcry_mpi_new(0);
423 /* Packet size should be: Session ID + Ma + Encrypted client nonce */
424 if (ibuflen != 2 + PRIMEBITS/8 + 16) {
425 LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
430 /* Skip session id */
433 /* Extract Mb, client's "public" key */
434 gcry_mpi_scan(&Mb, GCRYMPI_FMT_USG, ibuf, PRIMEBITS/8, NULL);
437 /* Now finally generate the Key: K = Mb^Ra mod p */
438 gcry_mpi_powm(K, Mb, Ra, p);
440 /* We need K in binary form in order to ... */
441 K_bin = calloc(1, PRIMEBITS/8);
446 gcry_mpi_print(GCRYMPI_FMT_USG, K_bin, PRIMEBITS/8, &nwritten, K);
447 if (nwritten < PRIMEBITS/8) {
448 memmove(K_bin + PRIMEBITS/8 - nwritten, K_bin, nwritten);
449 memset(K_bin, 0, PRIMEBITS/8 - nwritten);
452 /* ... generate the MD5 hash of K. K_MD5hash is what we actually use ! */
453 K_MD5hash = calloc(1, K_hash_len = gcry_md_get_algo_dlen(GCRY_MD_MD5));
454 if (K_MD5hash == NULL) {
458 gcry_md_hash_buffer(GCRY_MD_MD5, K_MD5hash, K_bin, PRIMEBITS/8);
462 /* FIXME: To support the Reconnect UAM, we need to store this key somewhere */
464 /* Set up our encryption context. */
465 ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
466 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
471 ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
472 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
476 /* Set the initialization vector for client->server transfer. */
477 ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
478 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
482 /* Finally: decrypt client's md5_K(client nonce, C2SIV) inplace */
483 ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16, NULL, 0);
484 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
488 /* Pull out clients nonce */
489 gcry_mpi_scan(&clientNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
490 /* Increment nonce */
491 gcry_mpi_add_ui(clientNonce, clientNonce, 1);
493 /* Generate our nonce and remember it for Logincont2 */
494 gcry_create_nonce(serverNonce_bin, 16); /* We'll use this here */
495 gcry_mpi_scan(&serverNonce, GCRYMPI_FMT_USG, serverNonce_bin, 16, NULL); /* For use in Logincont2 */
497 /* ---- Start building reply packet ---- */
499 /* Session ID + 1 first */
500 uint16 = htons(ID+1);
501 memcpy(rbuf, &uint16, sizeof(uint16_t));
505 /* Client nonce + 1 */
506 gcry_mpi_print(GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, NULL, clientNonce);
508 memcpy(rbuf+16, serverNonce_bin, 16);
510 /* Set the initialization vector for server->client transfer. */
511 ctxerror = gcry_cipher_setiv(ctx, dhx_s2civ, sizeof(dhx_s2civ));
512 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
516 /* Encrypt md5_K(clientNonce+1, serverNonce) inplace */
517 ctxerror = gcry_cipher_encrypt(ctx, rbuf, 32, NULL, 0);
518 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
525 ret = AFPERR_AUTHCONT;
529 gcry_cipher_close(ctx);
531 gcry_mpi_release(serverNonce);
536 gcry_mpi_release(Mb);
537 gcry_mpi_release(Ra);
538 gcry_mpi_release(clientNonce);
543 * Try to authenticate via PAM as "adminauthuser"
545 static int loginasroot(const char *adminauthuser, const char **hostname, int status)
549 if ((PAM_error = pam_end(pamh, status)) != PAM_SUCCESS)
553 if ((PAM_error = pam_start("netatalk", adminauthuser, &PAM_conversation, &pamh)) != PAM_SUCCESS) {
554 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s", pam_strerror(pamh,PAM_error));
558 /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */
559 pam_set_item(pamh, PAM_TTY, "afpd");
560 pam_set_item(pamh, PAM_RHOST, *hostname);
561 if ((PAM_error = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
564 LOG(log_warning, logtype_uams, "DHX2: Authenticated as \"%s\"", adminauthuser);
570 static int logincont2(void *obj_in, struct passwd **uam_pwd,
571 char *ibuf, size_t ibuflen,
572 char *rbuf _U_, size_t *rbuflen)
574 AFPObj *obj = obj_in;
575 int ret = AFPERR_MISC;
577 const char *hostname = NULL;
578 gcry_mpi_t retServerNonce;
579 gcry_cipher_hd_t ctx;
580 gcry_error_t ctxerror;
581 char *utfpass = NULL;
585 /* Packet size should be: Session ID + ServerNonce + Passwd buffer (evantually +10 extra bytes, see Apples Docs) */
586 if ((ibuflen != 2 + 16 + 256) && (ibuflen != 2 + 16 + 256 + 10)) {
587 LOG(log_error, logtype_uams, "DHX2: Paket length not correct: %u. Should be 274 or 284.", ibuflen);
592 retServerNonce = gcry_mpi_new(0);
595 uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL);
597 /* Set up our encryption context. */
598 ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
599 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
604 ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
605 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
609 /* Set the initialization vector for client->server transfer. */
610 ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
611 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
616 /* Skip Session ID */
619 /* Finally: decrypt client's md5_K(serverNonce+1, passwor, C2SIV) inplace */
620 ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16+256, NULL, 0);
621 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
625 /* Pull out nonce. Should be serverNonce+1 */
626 gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
627 gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1);
628 if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) {
630 ret = AFPERR_NOTAUTH;
635 /* ---- Start authentication with PAM --- */
637 /* The password is in legacy Mac encoding, convert it to host encoding */
638 if (convert_string_allocate(CH_MAC, CH_UNIX, ibuf, -1, &utfpass) == (size_t)-1) {
639 LOG(log_error, logtype_uams, "DHX2: conversion error");
642 PAM_password = utfpass;
645 LOG(log_maxdebug, logtype_default, "DHX2: password: %s", PAM_password);
648 /* Set these things up for the conv function */
650 ret = AFPERR_NOTAUTH;
651 PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation, &pamh);
652 if (PAM_error != PAM_SUCCESS) {
653 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s", pam_strerror(pamh,PAM_error));
657 /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */
658 pam_set_item(pamh, PAM_TTY, "afpd");
659 pam_set_item(pamh, PAM_RHOST, hostname);
660 pam_set_item(pamh, PAM_RUSER, PAM_username);
662 PAM_error = pam_authenticate(pamh, 0);
663 if (PAM_error != PAM_SUCCESS) {
664 if (PAM_error == PAM_MAXTRIES)
665 ret = AFPERR_PWDEXPR;
666 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s", pam_strerror(pamh, PAM_error));
668 if (!obj->options.adminauthuser)
670 if (loginasroot(obj->options.adminauthuser, &hostname, PAM_error) != PAM_SUCCESS) {
675 PAM_error = pam_acct_mgmt(pamh, 0);
676 if (PAM_error != PAM_SUCCESS ) {
677 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
678 pam_strerror(pamh, PAM_error));
679 if (PAM_error == PAM_NEW_AUTHTOK_REQD) /* password expired */
680 ret = AFPERR_PWDEXPR;
681 #ifdef PAM_AUTHTOKEN_REQD
682 else if (PAM_error == PAM_AUTHTOKEN_REQD)
683 ret = AFPERR_PWDCHNG;
688 #ifndef PAM_CRED_ESTABLISH
689 #define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
691 PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
692 if (PAM_error != PAM_SUCCESS) {
693 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
694 pam_strerror(pamh, PAM_error));
698 PAM_error = pam_open_session(pamh, 0);
699 if (PAM_error != PAM_SUCCESS) {
700 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
701 pam_strerror(pamh, PAM_error));
705 memset(ibuf, 0, 256); /* zero out the password */
707 memset(utfpass, 0, strlen(utfpass));
709 LOG(log_info, logtype_uams, "DHX2: PAM Auth OK!");
714 gcry_cipher_close(ctx);
716 if (utfpass) free(utfpass);
719 gcry_mpi_release(serverNonce);
720 gcry_mpi_release(retServerNonce);
724 static int pam_logincont(void *obj, struct passwd **uam_pwd,
725 char *ibuf, size_t ibuflen,
726 char *rbuf, size_t *rbuflen)
731 /* check for session id */
732 memcpy(&retID, ibuf, sizeof(uint16_t));
733 retID = ntohs(retID);
735 ret = logincont1(obj, ibuf, ibuflen, rbuf, rbuflen);
736 else if (retID == ID+1)
737 ret = logincont2(obj, uam_pwd, ibuf,ibuflen, rbuf, rbuflen);
739 LOG(log_info, logtype_uams, "DHX2: Session ID Mismatch");
747 static void pam_logout(void) {
748 pam_close_session(pamh, 0);
753 /****************************
754 * --- Change pwd stuff --- */
756 static int changepw_1(void *obj, char *uname,
757 char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
761 /* Remember it now, use it in changepw_3 */
762 PAM_username = uname;
763 return( dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen) );
766 static int changepw_2(void *obj,
767 char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
769 return( logincont1(obj, ibuf, ibuflen, rbuf, rbuflen) );
772 static int changepw_3(void *obj _U_,
773 char *ibuf, size_t ibuflen _U_,
774 char *rbuf _U_, size_t *rbuflen _U_)
780 const char *hostname = NULL;
781 gcry_mpi_t retServerNonce;
782 gcry_cipher_hd_t ctx;
783 gcry_error_t ctxerror;
787 LOG(log_error, logtype_uams, "DHX2 ChangePW: packet 3 processing");
789 /* Packet size should be: Session ID + ServerNonce + 2*Passwd buffer */
790 if (ibuflen != 2 + 16 + 2*256) {
791 LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
796 retServerNonce = gcry_mpi_new(0);
799 uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL);
801 /* Set up our encryption context. */
802 ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
803 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
808 ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
809 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
814 /* Set the initialization vector for client->server transfer. */
815 ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
816 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
821 /* Skip Session ID */
824 /* Finally: decrypt client's md5_K(serverNonce+1, 2*password, C2SIV) inplace */
825 ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16+2*256, NULL, 0);
826 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
830 /* Pull out nonce. Should be serverNonce+1 */
831 gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
832 gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1);
833 if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) {
835 ret = AFPERR_NOTAUTH;
840 /* ---- Start pwd changing with PAM --- */
841 ibuf[255] = '\0'; /* For safety */
844 /* check if new and old password are equal */
845 if (memcmp(ibuf, ibuf + 256, 255) == 0) {
846 LOG(log_info, logtype_uams, "DHX2 Chgpwd: new and old password are equal");
847 ret = AFPERR_PWDSAME;
851 /* Set these things up for the conv function. PAM_username was set in changepw_1 */
852 PAM_password = ibuf + 256;
853 PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation, &lpamh);
854 if (PAM_error != PAM_SUCCESS) {
855 LOG(log_info, logtype_uams, "DHX2 Chgpwd: PAM error in pam_start");
859 pam_set_item(lpamh, PAM_TTY, "afpd");
860 uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL);
861 pam_set_item(lpamh, PAM_RHOST, hostname);
864 PAM_error = pam_authenticate(lpamh,0);
865 if (PAM_error != PAM_SUCCESS) {
866 LOG(log_info, logtype_uams, "DHX2 Chgpwd: error authenticating with PAM");
868 pam_end(lpamh, PAM_error);
869 ret = AFPERR_NOTAUTH;
873 PAM_error = pam_chauthtok(lpamh, 0);
874 seteuid(uid); /* un-root ourselves. */
875 memset(ibuf, 0, 512);
876 if (PAM_error != PAM_SUCCESS) {
877 LOG(log_info, logtype_uams, "DHX2 Chgpwd: error changing pw with PAM");
878 pam_end(lpamh, PAM_error);
886 gcry_cipher_close(ctx);
890 gcry_mpi_release(serverNonce);
891 gcry_mpi_release(retServerNonce);
895 static int dhx2_changepw(void *obj _U_, char *uname,
896 struct passwd *pwd _U_, char *ibuf, size_t ibuflen _U_,
897 char *rbuf _U_, size_t *rbuflen _U_)
899 /* We use this to serialize the three incoming FPChangePassword calls */
900 static int dhx2_changepw_status = 1;
902 int ret = AFPERR_NOTAUTH; /* gcc can't figure out it's always initialized */
904 switch (dhx2_changepw_status) {
906 ret = changepw_1( obj, uname, ibuf, ibuflen, rbuf, rbuflen);
907 if ( ret == AFPERR_AUTHCONT)
908 dhx2_changepw_status = 2;
911 ret = changepw_2( obj, ibuf, ibuflen, rbuf, rbuflen);
912 if ( ret == AFPERR_AUTHCONT)
913 dhx2_changepw_status = 3;
915 dhx2_changepw_status = 1;
918 ret = changepw_3( obj, ibuf, ibuflen, rbuf, rbuflen);
919 dhx2_changepw_status = 1; /* Whether is was succesfull or not: we
926 static int uam_setup(const char *path)
928 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHX2", pam_login,
929 pam_logincont, pam_logout, pam_login_ext) < 0)
931 if (uam_register(UAM_SERVER_CHANGEPW, path, "DHX2", dhx2_changepw) < 0)
934 LOG(log_debug, logtype_uams, "DHX2: generating mersenne primes");
935 /* Generate p and g for DH */
936 if (dh_params_generate(PRIMEBITS) != 0) {
937 LOG(log_error, logtype_uams, "DHX2: Couldn't generate p and g");
944 static void uam_cleanup(void)
946 uam_unregister(UAM_SERVER_LOGIN, "DHX2");
947 uam_unregister(UAM_SERVER_CHANGEPW, "DHX2");
949 LOG(log_debug, logtype_uams, "DHX2: uam_cleanup");
955 UAM_MODULE_EXPORT struct uam_export uams_dhx2 = {
958 uam_setup, uam_cleanup
962 UAM_MODULE_EXPORT struct uam_export uams_dhx2_pam = {
965 uam_setup, uam_cleanup
968 #endif /* USE_PAM && UAM_DHX2 */