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.
11 #if defined(USE_PAM) && defined(UAM_DHX)
17 #include <security/pam_appl.h>
23 #include <atalk/afp.h>
24 #include <atalk/uam.h>
28 #define CRYPTBUFLEN (KEYSIZE*2)
29 #define CRYPT2BUFLEN (KEYSIZE + PASSWDLEN)
30 #define CHANGEPWBUFLEN (KEYSIZE + 2*PASSWDLEN)
32 /* hash a number to a 16-bit quantity */
33 #define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
34 (unsigned long) (a)) & 0xffff)
37 static CAST_KEY castkey;
38 static struct passwd *dhxpwd;
39 static u_int8_t randbuf[KEYSIZE];
41 /* diffie-hellman bits */
42 static unsigned char msg2_iv[] = "CJalbert";
43 static unsigned char msg3_iv[] = "LWallace";
44 static const u_int8_t p[] = {0xBA, 0x28, 0x73, 0xDF, 0xB0, 0x60, 0x57, 0xD4,
45 0x3F, 0x20, 0x24, 0x74, 0x4C, 0xEE, 0xE7, 0x5B};
46 static const u_int8_t g = 0x07;
49 /* Static variables used to communicate between the conversation function
50 * and the server_login function
52 static pam_handle_t *pamh = NULL;
53 static char *PAM_username;
54 static char *PAM_password;
56 /* PAM conversation function
57 * Here we assume (for now, at least) that echo on means login name, and
58 * echo off means password.
60 static int PAM_conv (int num_msg,
61 const struct pam_message **msg,
62 struct pam_response **resp,
65 struct pam_response *reply;
67 #define COPY_STRING(s) (s) ? strdup(s) : NULL
72 reply = (struct pam_response *)
73 calloc(num_msg, sizeof(struct pam_response));
78 for (count = 0; count < num_msg; count++) {
81 switch (msg[count]->msg_style) {
82 case PAM_PROMPT_ECHO_ON:
83 if (!(string = COPY_STRING(PAM_username)))
86 case PAM_PROMPT_ECHO_OFF:
87 if (!(string = COPY_STRING(PAM_password)))
91 #ifdef PAM_BINARY_PROMPT
92 case PAM_BINARY_PROMPT:
102 reply[count].resp_retcode = 0;
103 reply[count].resp = string;
112 for (count = 0; count < num_msg; count++) {
113 if (!reply[count].resp)
115 switch (msg[count]->msg_style) {
116 case PAM_PROMPT_ECHO_OFF:
117 case PAM_PROMPT_ECHO_ON:
118 free(reply[count].resp);
126 static struct pam_conv PAM_conversation = {
132 static int dhx_setup(void *obj, char *ibuf, int ibuflen,
133 char *rbuf, int *rbuflen)
137 BIGNUM *bn, *gbn, *pbn;
140 /* get the client's public key */
141 if (!(bn = BN_bin2bn(ibuf, KEYSIZE, NULL)))
145 if (!(gbn = BN_bin2bn(&g, sizeof(g), NULL))) {
150 if (!(pbn = BN_bin2bn(p, sizeof(p), NULL))) {
156 /* okay, we're ready */
157 if (!(dh = DH_new())) {
164 /* generate key and make sure that we have enough space */
167 if (!DH_generate_key(dh) || (BN_num_bytes(dh->pub_key) > KEYSIZE)) {
171 /* figure out the key. store the key in rbuf for now. */
172 i = DH_compute_key(rbuf, bn, dh);
175 CAST_set_key(&castkey, i, rbuf);
177 /* session id. it's just a hashed version of the object pointer. */
178 sessid = dhxhash(obj);
179 memcpy(rbuf, &sessid, sizeof(sessid));
180 rbuf += sizeof(sessid);
181 *rbuflen += sizeof(sessid);
184 BN_bn2bin(dh->pub_key, rbuf);
188 /* buffer to be encrypted */
190 if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM, (void *) randbuf,
195 memcpy(rbuf, &randbuf, sizeof(randbuf));
197 /* get the signature. it's always 16 bytes. */
199 if (uam_afpserver_option(obj, UAM_OPTION_SIGNATURE,
200 (void *) &buf, NULL) < 0) {
204 memcpy(rbuf + KEYSIZE, buf, KEYSIZE);
206 memset(rbuf + KEYSIZE, 0, KEYSIZE);
209 /* encrypt using cast */
210 CAST_cbc_encrypt(rbuf, rbuf, CRYPTBUFLEN, &castkey, msg2_iv,
212 *rbuflen += CRYPTBUFLEN;
215 return AFPERR_AUTHCONT;
224 /* dhx login: things are done in a slightly bizarre order to avoid
225 * having to clean things up if there's an error. */
226 static int pam_login(void *obj, struct passwd **uam_pwd,
227 char *ibuf, int ibuflen,
228 char *rbuf, int *rbuflen)
235 /* grab some of the options */
236 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &buf,
240 len = (unsigned char) *ibuf++;
242 return( AFPERR_PARAM );
245 memcpy(buf, ibuf, len );
248 if ((unsigned long) ibuf & 1) /* pad to even boundary */
251 if (( dhxpwd = uam_getname(buf, i)) == NULL ) {
256 syslog( LOG_INFO, "dhx login: %s", buf);
257 return dhx_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
261 static int pam_logincont(void *obj, struct passwd **uam_pwd,
262 char *ibuf, int ibuflen,
263 char *rbuf, int *rbuflen)
266 BIGNUM *bn1, *bn2, *bn3;
272 /* check for session id */
273 memcpy(&sessid, ibuf, sizeof(sessid));
274 if (sessid != dhxhash(obj))
276 ibuf += sizeof(sessid);
278 if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
279 (void *) &hostname, NULL) < 0)
282 CAST_cbc_encrypt(ibuf, rbuf, CRYPT2BUFLEN, &castkey,
283 msg3_iv, CAST_DECRYPT);
284 memset(&castkey, 0, sizeof(castkey));
286 /* check to make sure that the random number is the same. we
287 * get sent back an incremented random number. */
288 if (!(bn1 = BN_bin2bn(rbuf, KEYSIZE, NULL)))
291 if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
296 /* zero out the random number */
297 memset(rbuf, 0, sizeof(randbuf));
298 memset(randbuf, 0, sizeof(randbuf));
301 if (!(bn3 = BN_new())) {
307 BN_sub(bn3, bn1, bn2);
311 /* okay. is it one more? */
312 if (!BN_is_one(bn3)) {
318 /* Set these things up for the conv function */
319 rbuf[PASSWDLEN] = '\0';
322 err = AFPERR_NOTAUTH;
323 PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation,
325 if (PAM_error != PAM_SUCCESS)
328 /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */
329 pam_set_item(pamh, PAM_TTY, "afpd");
330 pam_set_item(pamh, PAM_RHOST, hostname);
331 PAM_error = pam_authenticate(pamh,0);
332 if (PAM_error != PAM_SUCCESS) {
333 if (PAM_error == PAM_MAXTRIES)
334 err = AFPERR_PWDEXPR;
338 PAM_error = pam_acct_mgmt(pamh, 0);
339 if (PAM_error != PAM_SUCCESS) {
340 if (PAM_error == PAM_ACCT_EXPIRED)
341 err = AFPERR_PWDEXPR;
342 #ifdef PAM_AUTHTOKEN_REQD
343 else if (PAM_error == PAM_AUTHTOKEN_REQD)
344 err = AFPERR_PWDCHNG;
349 #ifndef PAM_CRED_ESTABLISH
350 #define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
352 PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
353 if (PAM_error != PAM_SUCCESS)
356 PAM_error = pam_open_session(pamh, 0);
357 if (PAM_error != PAM_SUCCESS)
360 memset(rbuf, 0, PASSWDLEN); /* zero out the password */
365 pam_end(pamh, PAM_error);
367 memset(rbuf, 0, CRYPT2BUFLEN);
372 static void pam_logout() {
373 pam_close_session(pamh, 0);
379 /* change pw for dhx needs a couple passes to get everything all
380 * right. basically, it's like the login/logincont sequence */
381 static int pam_changepw(void *obj, char *username,
382 struct passwd *pwd, char *ibuf, int ibuflen,
383 char *rbuf, int *rbuflen)
385 BIGNUM *bn1, *bn2, *bn3;
394 memcpy(&sessid, ibuf, sizeof(sessid));
395 ibuf += sizeof(sessid);
397 if (!sessid) { /* no sessid -> initialization phase */
398 PAM_username = username;
399 ibuflen -= sizeof(sessid);
400 return dhx_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
404 /* otherwise, it's like logincont but different. */
406 /* check out the session id */
407 if (sessid != dhxhash(obj))
410 /* we need this for pam */
411 if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
412 (void *) &hostname, NULL) < 0)
415 /* grab the client's nonce, old password, and new password. */
416 CAST_cbc_encrypt(ibuf, ibuf, CHANGEPWBUFLEN, &castkey,
417 msg3_iv, CAST_DECRYPT);
418 memset(&castkey, 0, sizeof(castkey));
420 /* check to make sure that the random number is the same. we
421 * get sent back an incremented random number. */
422 if (!(bn1 = BN_bin2bn(ibuf, KEYSIZE, NULL)))
425 if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
430 /* zero out the random number */
431 memset(rbuf, 0, sizeof(randbuf));
432 memset(randbuf, 0, sizeof(randbuf));
434 if (!(bn3 = BN_new())) {
440 BN_sub(bn3, bn1, bn2);
444 /* okay. is it one more? */
446 if (!BN_is_one(bn3)) {
453 /* Set these things up for the conv function. the old password
456 ibuf[PASSWDLEN + PASSWDLEN] = '\0';
457 PAM_password = ibuf + PASSWDLEN;
459 PAM_error = pam_start("netatalk", username, &PAM_conversation,
461 if (PAM_error != PAM_SUCCESS)
463 pam_set_item(lpamh, PAM_TTY, "afpd");
464 pam_set_item(lpamh, PAM_RHOST, hostname);
466 /* we might need to do this as root */
469 PAM_error = pam_authenticate(lpamh, 0);
470 if (PAM_error != PAM_SUCCESS) {
472 pam_end(lpamh, PAM_error);
473 return AFPERR_NOTAUTH;
476 /* clear out old passwd */
477 memset(ibuf + PASSWDLEN, 0, PASSWDLEN);
481 ibuf[PASSWDLEN] = '\0';
483 /* this really does need to be done as root */
484 PAM_error = pam_chauthtok(lpamh, 0);
485 seteuid(uid); /* un-root ourselves. */
486 memset(ibuf, 0, PASSWDLEN);
487 if (PAM_error != PAM_SUCCESS) {
488 pam_end(lpamh, PAM_error);
489 return AFPERR_ACCESS;
497 static int uam_setup(const char *path)
499 if (uam_register(UAM_SERVER_LOGIN, path, "DHCAST128", pam_login,
500 pam_logincont, pam_logout) < 0)
503 if (uam_register(UAM_SERVER_CHANGEPW, path, "DHCAST128",
505 uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
509 /*uam_register(UAM_SERVER_PRINTAUTH, path, "DHCAST128",
515 static void uam_cleanup(void)
517 uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
518 uam_unregister(UAM_SERVER_CHANGEPW, "DHCAST128");
519 /*uam_unregister(UAM_SERVER_PRINTAUTH, "DHCAST128"); */
522 UAM_MODULE_EXPORT struct uam_export uams_dhx = {
525 uam_setup, uam_cleanup
528 #endif /* USE_PAM && UAM_DHX */