2 * $Id: uams_dhx2_pam.c,v 1.1 2008-11-22 12:07:26 didg Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
6 * All Rights Reserved. See COPYRIGHT.
11 #endif /* HAVE_CONFIG_H */
13 #if defined (USE_PAM) && defined (UAM_DHX2)
17 #include <atalk/logger.h>
21 #endif /* HAVE_UNISTD_H */
23 #ifdef HAVE_SECURITY_PAM_APPL_H
24 #include <security/pam_appl.h>
26 #ifdef HAVE_PAM_PAM_APPL_H
27 #include <pam/pam_appl.h>
33 #endif /* HAVE_LIBGCRYPT */
35 #include <atalk/afp.h>
36 #include <atalk/uam.h>
37 #include "../afpd/globals.h"
39 /* Number of bits for p which we generate. Everybode out there uses 512, so we beet them */
40 #define PRIMEBITS 1024
42 /* hash a number to a 16-bit quantity */
43 #define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
44 (unsigned long) (a)) & 0xffff)
46 /* Some parameters need be maintained across calls */
47 static gcry_mpi_t p, Ra;
48 static gcry_mpi_t serverNonce;
49 static char *K_MD5hash = NULL;
50 static int K_hash_len;
53 /* The initialization vectors for CAST128 are fixed by Apple. */
54 static unsigned char dhx_c2siv[] = { 'L', 'W', 'a', 'l', 'l', 'a', 'c', 'e' };
55 static unsigned char dhx_s2civ[] = { 'C', 'J', 'a', 'l', 'b', 'e', 'r', 't' };
57 /* Static variables used to communicate between the conversation function
58 * and the server_login function */
59 static pam_handle_t *pamh = NULL;
60 static char *PAM_username;
61 static char *PAM_password;
62 static struct passwd *dhxpwd;
64 /*********************************************************
65 * Crypto helper func to generate p and g for use in DH.
66 * libgcrpyt doesn't provide one directly.
67 * Algorithm taken from GNUTLS:gnutls_dh_primes.c
68 *********************************************************/
71 * This function will generate a new pair of prime and generator for use in
72 * the Diffie-Hellman key exchange.
73 * The bits value should be one of 768, 1024, 2048, 3072 or 4096.
77 dh_params_generate (gcry_mpi_t *ret_p, gcry_mpi_t *ret_g, unsigned int bits) {
79 int result, times = 0, qbits;
81 gcry_mpi_t g = NULL, prime = NULL;
82 gcry_mpi_t *factors = NULL;
85 /* Version check should be the very first call because it
86 makes sure that important subsystems are intialized. */
87 if (!gcry_check_version (GCRYPT_VERSION)) {
88 LOG(log_info, logtype_uams, "PAM DHX2: libgcrypt versions mismatch. Need: %u", GCRYPT_VERSION);
96 qbits = (bits / 40) + 105;
98 if (qbits & 1) /* better have an even number */
101 /* find a prime number of size bits. */
104 gcry_mpi_release (prime);
105 gcry_prime_release_factors (factors);
107 err = gcry_prime_generate (&prime, bits, qbits, &factors, NULL, NULL,
108 GCRY_STRONG_RANDOM, GCRY_PRIME_FLAG_SPECIAL_FACTOR);
110 result = AFPERR_MISC;
113 err = gcry_prime_check (prime, 0);
115 } while (err != 0 && times < 10);
118 result = AFPERR_MISC;
122 /* generate the group generator. */
123 err = gcry_prime_group_generator (&g, prime, factors, NULL);
125 result = AFPERR_MISC;
129 gcry_prime_release_factors (factors);
135 gcry_mpi_release (g);
139 gcry_mpi_release (prime);
144 gcry_prime_release_factors (factors);
145 gcry_mpi_release (g);
146 gcry_mpi_release (prime);
152 /* PAM conversation function
153 * Here we assume (for now, at least) that echo on means login name, and
154 * echo off means password.
156 static int PAM_conv (int num_msg,
157 const struct pam_message **msg,
158 struct pam_response **resp,
159 void *appdata_ptr _U_) {
161 struct pam_response *reply;
163 #define COPY_STRING(s) (s) ? strdup(s) : NULL
169 LOG(log_info, logtype_uams, "PAM DHX2 Conversation Err -- %s",
175 reply = (struct pam_response *)
176 calloc(num_msg, sizeof(struct pam_response));
180 LOG(log_info, logtype_uams, "PAM DHX2: Conversation Err -- %s",
186 for (count = 0; count < num_msg; count++) {
189 switch (msg[count]->msg_style) {
190 case PAM_PROMPT_ECHO_ON:
191 if (!(string = COPY_STRING(PAM_username))) {
193 LOG(log_info, logtype_uams, "PAM DHX2: username failure -- %s",
199 case PAM_PROMPT_ECHO_OFF:
200 if (!(string = COPY_STRING(PAM_password))) {
202 LOG(log_info, logtype_uams, "PAM DHX2: passwd failure: --: %s",
209 #ifdef PAM_BINARY_PROMPT
210 case PAM_BINARY_PROMPT:
211 #endif /* PAM_BINARY_PROMPT */
216 LOG(log_info, logtype_uams, "PAM DHX2: Binary_Prompt -- %s", strerror(errno));
221 reply[count].resp_retcode = 0;
222 reply[count].resp = string;
228 LOG(log_info, logtype_uams, "PAM DHX2: PAM Success");
232 for (count = 0; count < num_msg; count++) {
233 if (!reply[count].resp)
235 switch (msg[count]->msg_style) {
236 case PAM_PROMPT_ECHO_OFF:
237 case PAM_PROMPT_ECHO_ON:
238 free(reply[count].resp);
244 LOG(log_info, logtype_uams, "PAM DHX2: Conversation Err -- %s",
250 static struct pam_conv PAM_conversation = {
256 static int dhx2_setup(void *obj, char *ibuf, int ibuflen _U_,
257 char *rbuf, int *rbuflen)
263 char *Ra_binary = NULL;
264 gcry_cipher_hd_t ctx;
265 gcry_error_t ctxerror;
275 Ra = gcry_mpi_new(0);
276 Ma = gcry_mpi_new(0);
278 /* Generate p and g for DH */
279 ret = dh_params_generate( &p, &g, PRIMEBITS);
281 LOG(log_info, logtype_uams, "DHX2: Couldn't generate p and g");
286 /* Generate our random number Ra. */
287 Ra_binary = calloc(1, PRIMEBITS/8);
288 if (Ra_binary == NULL) {
292 gcry_randomize(Ra_binary, PRIMEBITS/8, GCRY_STRONG_RANDOM);
293 gcry_mpi_scan(&Ra, GCRYMPI_FMT_USG, Ra_binary, PRIMEBITS/8, NULL);
297 /* Ma = g^Ra mod p. This is our "public" key */
298 gcry_mpi_powm(Ma, g, Ra, p);
300 /* ------- DH Init done ------ */
301 /* Start building reply packet */
303 /* Session ID first */
305 *(u_int16_t *)rbuf = htons(ID);
310 gcry_mpi_print( GCRYMPI_FMT_USG, rbuf, 4, &nwritten, g);
312 memmove( rbuf+4-nwritten, rbuf, nwritten);
313 memset( rbuf, 0, 4-nwritten);
318 /* len = length of p = PRIMEBITS/8 */
319 *(u_int16_t *)rbuf = htons((u_int16_t) PRIMEBITS/8);
324 gcry_mpi_print( GCRYMPI_FMT_USG, rbuf, PRIMEBITS/8, NULL, p);
326 *rbuflen += PRIMEBITS/8;
329 gcry_mpi_print( GCRYMPI_FMT_USG, rbuf, PRIMEBITS/8, &len, Ma);
330 if (len < PRIMEBITS/8) {
331 memmove(rbuf + (PRIMEBITS/8) - len, rbuf, len);
332 memset(rbuf, 0, (PRIMEBITS/8) - len);
335 *rbuflen += PRIMEBITS/8;
337 ret = AFPERR_AUTHCONT;
339 error: /* We exit here anyway */
340 /* We will only need p and Ra later, but mustn't forget to release it ! */
342 gcry_mpi_release(Ma);
346 /* -------------------------------- */
347 static int login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_,
348 char *ibuf, int ibuflen,
349 char *rbuf, int *rbuflen)
351 if (( dhxpwd = uam_getname(obj, username, ulen)) == NULL ) {
352 LOG(log_info, logtype_uams, "DHX2: unknown username");
356 PAM_username = username;
357 LOG(log_info, logtype_uams, "DHX2 login: %s", username);
358 return dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
361 /* -------------------------------- */
362 /* dhx login: things are done in a slightly bizarre order to avoid
363 * having to clean things up if there's an error. */
364 static int pam_login(void *obj, struct passwd **uam_pwd,
365 char *ibuf, int ibuflen,
366 char *rbuf, int *rbuflen)
373 /* grab some of the options */
374 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
375 LOG(log_info, logtype_uams, "DHX2: uam_afpserver_option didn't meet uam_option_username -- %s",
380 len = (unsigned char) *ibuf++;
382 LOG(log_info, logtype_uams, "DHX2: Signature Retieval Failure -- %s",
387 memcpy(username, ibuf, len );
389 username[ len ] = '\0';
391 if ((unsigned long) ibuf & 1) /* pad to even boundary */
394 return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
397 /* ----------------------------- */
398 static int pam_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
399 char *ibuf, int ibuflen,
400 char *rbuf, int *rbuflen)
408 /* grab some of the options */
409 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
410 LOG(log_info, logtype_uams, "DHX2: uam_afpserver_option didn't meet uam_option_username -- %s",
418 memcpy(&temp16, uname, sizeof(temp16));
421 if ( !len || len > ulen ) {
422 LOG(log_info, logtype_uams, "DHX2: Signature Retrieval Failure -- %s",
426 memcpy(username, uname +2, len );
427 username[ len ] = '\0';
429 return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
432 /* -------------------------------- */
434 static int logincont1(void *obj, char *ibuf, int ibuflen, char *rbuf, int *rbuflen)
441 gcry_mpi_t Mb, K, clientNonce;
443 char serverNonce_bin[16];
444 gcry_cipher_hd_t ctx;
445 gcry_error_t ctxerror;
447 Mb = gcry_mpi_new(0);
449 clientNonce = gcry_mpi_new(0);
450 serverNonce = gcry_mpi_new(0);
452 /* Packet size should be: Session ID + Ma + Encrypted client nonce */
453 if (ibuflen != 2 + PRIMEBITS/8 + 16) {
454 LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
459 /* Skip session id */
462 /* Extract Mb, client's "public" key */
463 gcry_mpi_scan(&Mb, GCRYMPI_FMT_USG, ibuf, PRIMEBITS/8, NULL);
466 /* Now finally generate the Key: K = Mb^Ra mod p */
467 gcry_mpi_powm(K, Mb, Ra, p);
469 /* We need K in binary form in order to ... */
470 K_bin = calloc(1, PRIMEBITS/8);
475 gcry_mpi_print(GCRYMPI_FMT_USG, K_bin, PRIMEBITS/8, &nwritten, K);
476 if (nwritten < PRIMEBITS/8) {
477 memmove(K_bin + PRIMEBITS/8 - nwritten, K_bin, nwritten);
478 memset(K_bin, 0, PRIMEBITS/8 - nwritten);
481 /* ... generate the MD5 hash of K. K_MD5hash is what we actually use ! */
482 K_MD5hash = calloc(1, K_hash_len = gcry_md_get_algo_dlen(GCRY_MD_MD5));
483 if (K_MD5hash == NULL) {
487 gcry_md_hash_buffer(GCRY_MD_MD5, K_MD5hash, K_bin, PRIMEBITS/8);
491 /* FIXME: To support the Reconnect UAM, we need to store this key somewhere */
493 /* Set up our encryption context. */
494 ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
495 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
500 ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
501 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
505 /* Set the initialization vector for client->server transfer. */
506 ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
507 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
511 /* Finally: decrypt client's md5_K(client nonce, C2SIV) inplace */
512 ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16, NULL, 0);
513 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
517 /* Pull out clients nonce */
518 gcry_mpi_scan(&clientNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
519 /* Increment nonce */
520 gcry_mpi_add_ui(clientNonce, clientNonce, 1);
522 /* Generate our nonce and remember it for Logincont2 */
523 gcry_create_nonce(serverNonce_bin, 16); /* We'll use this here */
524 gcry_mpi_scan(&serverNonce, GCRYMPI_FMT_USG, serverNonce_bin, 16, NULL); /* For use in Logincont2 */
526 /* ---- Start building reply packet ---- */
528 /* Session ID + 1 first */
529 *(u_int16_t *)rbuf = htons(ID+1);
533 /* Client nonce + 1 */
534 gcry_mpi_print(GCRYMPI_FMT_USG, rbuf, PRIMEBITS/8, NULL, clientNonce);
536 memcpy(rbuf+16, serverNonce_bin, 16);
538 /* Set the initialization vector for server->client transfer. */
539 ctxerror = gcry_cipher_setiv(ctx, dhx_s2civ, sizeof(dhx_s2civ));
540 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
544 /* Encrypt md5_K(clientNonce+1, serverNonce) inplace */
545 ctxerror = gcry_cipher_encrypt(ctx, rbuf, 32, NULL, 0);
546 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
553 ret = AFPERR_AUTHCONT;
557 gcry_cipher_close(ctx);
559 gcry_mpi_release(serverNonce);
564 gcry_mpi_release(Mb);
565 gcry_mpi_release(Ra);
567 gcry_mpi_release(clientNonce);
571 static int logincont2(void *obj, struct passwd **uam_pwd,
572 char *ibuf, int ibuflen,
573 char *rbuf, int *rbuflen)
577 char *hostname = NULL;
578 gcry_mpi_t retServerNonce;
579 gcry_cipher_hd_t ctx;
580 gcry_error_t ctxerror;
584 /* Packet size should be: Session ID + ServerNonce + Passwd buffer */
585 if (ibuflen != 2 + 16 + 256) {
586 LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
591 retServerNonce = gcry_mpi_new(0);
594 uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL);
596 /* Set up our encryption context. */
597 ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
598 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
603 ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
604 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
608 /* Set the initialization vector for client->server transfer. */
609 ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
610 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
615 /* Skip Session ID */
618 /* Finally: decrypt client's md5_K(serverNonce+1, passwor, C2SIV) inplace */
619 ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16+256, NULL, 0);
620 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
624 /* Pull out nonce. Should be serverNonce+1 */
625 gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
626 gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1);
627 if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) {
629 ret = AFPERR_NOTAUTH;
634 LOG(log_info, logtype_uams, "DHX2: logincont2 alive!");
636 /* ---- Start authentication with PAM --- */
638 /* Set these things up for the conv function */
641 ret = AFPERR_NOTAUTH;
642 PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation, &pamh);
643 if (PAM_error != PAM_SUCCESS) {
644 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
645 pam_strerror(pamh,PAM_error));
649 /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */
650 pam_set_item(pamh, PAM_TTY, "afpd");
651 pam_set_item(pamh, PAM_RHOST, hostname);
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",
657 pam_strerror(pamh, PAM_error));
661 PAM_error = pam_acct_mgmt(pamh, 0);
662 if (PAM_error != PAM_SUCCESS ) {
663 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
664 pam_strerror(pamh, PAM_error));
665 if (PAM_error == PAM_NEW_AUTHTOK_REQD) /* password expired */
666 ret = AFPERR_PWDEXPR;
667 #ifdef PAM_AUTHTOKEN_REQD
668 else if (PAM_error == PAM_AUTHTOKEN_REQD)
669 ret = AFPERR_PWDCHNG;
675 #ifndef PAM_CRED_ESTABLISH
676 #define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
678 PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
679 if (PAM_error != PAM_SUCCESS) {
680 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
681 pam_strerror(pamh, PAM_error));
685 PAM_error = pam_open_session(pamh, 0);
686 if (PAM_error != PAM_SUCCESS) {
687 LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
688 pam_strerror(pamh, PAM_error));
692 memset(ibuf, 0, 256); /* zero out the password */
694 LOG(log_info, logtype_uams, "DHX2: PAM Auth OK!");
695 if ( ret == AFPERR_PWDEXPR)
700 gcry_cipher_close(ctx);
704 gcry_mpi_release(serverNonce);
705 gcry_mpi_release(retServerNonce);
709 static int pam_logincont(void *obj, struct passwd **uam_pwd,
710 char *ibuf, int ibuflen,
711 char *rbuf, int *rbuflen)
716 /* check for session id */
717 retID = ntohs(*(u_int16_t *)ibuf);
719 ret = logincont1(obj, ibuf, ibuflen, rbuf, rbuflen);
720 else if (retID == ID+1)
721 ret = logincont2(obj, uam_pwd, ibuf,ibuflen, rbuf, rbuflen);
723 LOG(log_info, logtype_uams, "DHX2: Session ID Mismatch");
731 static void pam_logout() {
732 pam_close_session(pamh, 0);
737 /****************************
738 * --- Change pwd stuff --- */
740 static int changepw_1(void *obj, char *uname,
741 char *ibuf, int ibuflen, char *rbuf, int *rbuflen)
746 /* Remember it now, use it in changepw_3 */
747 PAM_username = uname;
748 LOG(log_error, logtype_uams, "DHX2 ChangePW: packet 1 processin for user: %s",PAM_username);
750 return( dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen) );
753 static int changepw_2(void *obj,
754 char *ibuf, int ibuflen, char *rbuf, int *rbuflen)
756 LOG(log_error, logtype_uams, "DHX2 ChangePW: packet 2 processing");
757 return( logincont1(obj, ibuf, ibuflen, rbuf, rbuflen) );
760 static int changepw_3(void *obj _U_,
761 char *ibuf, int ibuflen _U_,
762 char *rbuf _U_, int *rbuflen _U_)
768 char *hostname = NULL;
769 gcry_mpi_t retServerNonce;
770 gcry_cipher_hd_t ctx;
771 gcry_error_t ctxerror;
775 LOG(log_error, logtype_uams, "DHX2 ChangePW: packet 3 processing");
777 /* Packet size should be: Session ID + ServerNonce + 2*Passwd buffer */
778 if (ibuflen != 2 + 16 + 2*256) {
779 LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
784 retServerNonce = gcry_mpi_new(0);
787 uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL);
789 /* Set up our encryption context. */
790 ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
791 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
796 ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
797 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
802 /* Set the initialization vector for client->server transfer. */
803 ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
804 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
809 /* Skip Session ID */
812 /* Finally: decrypt client's md5_K(serverNonce+1, 2*password, C2SIV) inplace */
813 ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16+2*256, NULL, 0);
814 if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
818 /* Pull out nonce. Should be serverNonce+1 */
819 gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
820 gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1);
821 if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) {
823 ret = AFPERR_NOTAUTH;
828 /* ---- Start pwd changing with PAM --- */
829 ibuf[255] = '\0'; /* For safety */
832 LOG(log_info, logtype_uams, "DHX2 Chgpwd: new pwd \'%s\'",ibuf);
833 LOG(log_info, logtype_uams, "DHX2 Chgpwd: old pwd \'%s\'",ibuf+256);
835 /* check if new and old password are equal */
836 if (memcmp(ibuf, ibuf + 256, 255) == 0) {
837 LOG(log_info, logtype_uams, "DHX2 Chgpwd: new and old password are equal");
838 ret = AFPERR_PWDSAME;
842 /* Set these things up for the conv function. PAM_username was set in changepw_1 */
843 PAM_password = ibuf + 256;
844 PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation, &lpamh);
845 if (PAM_error != PAM_SUCCESS) {
846 LOG(log_info, logtype_uams, "DHX2 Chgpwd: PAM error in pam_start");
850 pam_set_item(lpamh, PAM_TTY, "afpd");
851 uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, (void *) &hostname, NULL);
852 pam_set_item(lpamh, PAM_RHOST, hostname);
855 PAM_error = pam_authenticate(lpamh,0);
856 if (PAM_error != PAM_SUCCESS) {
857 LOG(log_info, logtype_uams, "DHX2 Chgpwd: error authenticating with PAM");
859 pam_end(lpamh, PAM_error);
860 ret = AFPERR_NOTAUTH;
864 PAM_error = pam_chauthtok(lpamh, 0);
865 seteuid(uid); /* un-root ourselves. */
866 memset(ibuf, 0, 512);
867 if (PAM_error != PAM_SUCCESS) {
868 LOG(log_info, logtype_uams, "DHX2 Chgpwd: error changing pw with PAM");
869 pam_end(lpamh, PAM_error);
877 gcry_cipher_close(ctx);
882 gcry_mpi_release(serverNonce);
883 gcry_mpi_release(retServerNonce);
887 static int dhx2_changepw(void *obj _U_, char *uname,
888 struct passwd *pwd _U_, char *ibuf, int ibuflen _U_,
889 char *rbuf _U_, int *rbuflen _U_)
891 /* We use this to serialize the three incoming FPChangePassword calls */
892 static int dhx2_changepw_status = 1;
896 LOG(log_error, logtype_uams, "DHX2 ChangePW: Start!");
898 switch (dhx2_changepw_status) {
900 ret = changepw_1( obj, uname, ibuf, ibuflen, rbuf, rbuflen);
901 if ( ret == AFPERR_AUTHCONT)
902 dhx2_changepw_status += 1;
905 ret = changepw_2( obj, ibuf, ibuflen, rbuf, rbuflen);
906 if ( ret == AFPERR_AUTHCONT)
907 dhx2_changepw_status += 1;
909 dhx2_changepw_status = 1;
912 ret = changepw_3( obj, ibuf, ibuflen, rbuf, rbuflen);
913 dhx2_changepw_status = 1; /* Whether is was succesfull or not: we
920 static int uam_setup(const char *path)
922 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHX2", pam_login,
923 pam_logincont, pam_logout, pam_login_ext) < 0)
925 if (uam_register(UAM_SERVER_CHANGEPW, path, "DHX2", dhx2_changepw) < 0)
930 static void uam_cleanup(void)
932 uam_unregister(UAM_SERVER_LOGIN, "DHX2");
933 uam_unregister(UAM_SERVER_CHANGEPW, "DHX2");
937 UAM_MODULE_EXPORT struct uam_export uams_dhx2 = {
940 uam_setup, uam_cleanup
944 UAM_MODULE_EXPORT struct uam_export uams_dhx2_pam = {
947 uam_setup, uam_cleanup
950 #endif /* USE_PAM && UAM_DHX2 */