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.
6 #if defined(USE_PAM) && defined(UAM_DHX)
12 #include <security/pam_appl.h>
18 #include <atalk/afp.h>
19 #include <atalk/uam.h>
23 #define CRYPTBUFLEN (KEYSIZE*2)
24 #define CRYPT2BUFLEN (KEYSIZE + PASSWDLEN)
25 #define CHANGEPWBUFLEN (KEYSIZE + 2*PASSWDLEN)
27 /* hash a number to a 16-bit quantity */
28 #define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
29 (unsigned long) (a)) & 0xffff)
32 static CAST_KEY castkey;
33 static struct passwd *dhxpwd;
34 static u_int8_t randbuf[KEYSIZE];
36 /* diffie-hellman bits */
37 static unsigned char msg2_iv[] = "CJalbert";
38 static unsigned char msg3_iv[] = "LWallace";
39 static const u_int8_t p[] = {0xBA, 0x28, 0x73, 0xDF, 0xB0, 0x60, 0x57, 0xD4,
40 0x3F, 0x20, 0x24, 0x74, 0x4C, 0xEE, 0xE7, 0x5B};
41 static const u_int8_t g = 0x07;
44 /* Static variables used to communicate between the conversation function
45 * and the server_login function
47 static pam_handle_t *pamh = NULL;
48 static char *PAM_username;
49 static char *PAM_password;
51 /* PAM conversation function
52 * Here we assume (for now, at least) that echo on means login name, and
53 * echo off means password.
55 static int PAM_conv (int num_msg,
56 const struct pam_message **msg,
57 struct pam_response **resp,
60 struct pam_response *reply;
62 #define COPY_STRING(s) (s) ? strdup(s) : NULL
67 reply = (struct pam_response *)
68 calloc(num_msg, sizeof(struct pam_response));
73 for (count = 0; count < num_msg; count++) {
76 switch (msg[count]->msg_style) {
77 case PAM_PROMPT_ECHO_ON:
78 if (!(string = COPY_STRING(PAM_username)))
81 case PAM_PROMPT_ECHO_OFF:
82 if (!(string = COPY_STRING(PAM_password)))
86 #ifdef PAM_BINARY_PROMPT
87 case PAM_BINARY_PROMPT:
97 reply[count].resp_retcode = 0;
98 reply[count].resp = string;
107 for (count = 0; count < num_msg; count++) {
108 if (!reply[count].resp)
110 switch (msg[count]->msg_style) {
111 case PAM_PROMPT_ECHO_OFF:
112 case PAM_PROMPT_ECHO_ON:
113 free(reply[count].resp);
121 static struct pam_conv PAM_conversation = {
127 static int dhx_setup(void *obj, char *ibuf, int ibuflen,
128 char *rbuf, int *rbuflen)
132 BIGNUM *bn, *gbn, *pbn;
135 /* get the client's public key */
136 if (!(bn = BN_bin2bn(ibuf, KEYSIZE, NULL)))
140 if (!(gbn = BN_bin2bn(&g, sizeof(g), NULL))) {
145 if (!(pbn = BN_bin2bn(p, sizeof(p), NULL))) {
151 /* okay, we're ready */
152 if (!(dh = DH_new())) {
159 /* generate key and make sure that we have enough space */
162 if (!DH_generate_key(dh) || (BN_num_bytes(dh->pub_key) > KEYSIZE)) {
166 /* figure out the key. store the key in rbuf for now. */
167 i = DH_compute_key(rbuf, bn, dh);
170 CAST_set_key(&castkey, i, rbuf);
172 /* session id. it's just a hashed version of the object pointer. */
173 sessid = dhxhash(obj);
174 memcpy(rbuf, &sessid, sizeof(sessid));
175 rbuf += sizeof(sessid);
176 *rbuflen += sizeof(sessid);
179 BN_bn2bin(dh->pub_key, rbuf);
183 /* buffer to be encrypted */
185 if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM, (void *) randbuf,
190 memcpy(rbuf, &randbuf, sizeof(randbuf));
192 /* get the signature. it's always 16 bytes. */
194 if (uam_afpserver_option(obj, UAM_OPTION_SIGNATURE,
195 (void *) &buf, NULL) < 0) {
199 memcpy(rbuf + KEYSIZE, buf, KEYSIZE);
201 memset(rbuf + KEYSIZE, 0, KEYSIZE);
204 /* encrypt using cast */
205 CAST_cbc_encrypt(rbuf, rbuf, CRYPTBUFLEN, &castkey, msg2_iv,
207 *rbuflen += CRYPTBUFLEN;
210 return AFPERR_AUTHCONT;
219 /* dhx login: things are done in a slightly bizarre order to avoid
220 * having to clean things up if there's an error. */
221 static int pam_login(void *obj, struct passwd **uam_pwd,
222 char *ibuf, int ibuflen,
223 char *rbuf, int *rbuflen)
230 /* grab some of the options */
231 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &buf,
235 len = (unsigned char) *ibuf++;
237 return( AFPERR_PARAM );
240 memcpy(buf, ibuf, len );
243 if ((unsigned long) ibuf & 1) /* pad to even boundary */
246 if (( dhxpwd = uam_getname(buf, i)) == NULL ) {
251 syslog( LOG_INFO, "dhx login: %s", buf);
252 return dhx_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
256 static int pam_logincont(void *obj, struct passwd **uam_pwd,
257 char *ibuf, int ibuflen,
258 char *rbuf, int *rbuflen)
261 BIGNUM *bn1, *bn2, *bn3;
267 /* check for session id */
268 memcpy(&sessid, ibuf, sizeof(sessid));
269 if (sessid != dhxhash(obj))
271 ibuf += sizeof(sessid);
273 if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
274 (void *) &hostname, NULL) < 0)
277 CAST_cbc_encrypt(ibuf, rbuf, CRYPT2BUFLEN, &castkey,
278 msg3_iv, CAST_DECRYPT);
279 memset(&castkey, 0, sizeof(castkey));
281 /* check to make sure that the random number is the same. we
282 * get sent back an incremented random number. */
283 if (!(bn1 = BN_bin2bn(rbuf, KEYSIZE, NULL)))
286 if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
291 /* zero out the random number */
292 memset(rbuf, 0, sizeof(randbuf));
293 memset(randbuf, 0, sizeof(randbuf));
296 if (!(bn3 = BN_new())) {
302 BN_sub(bn3, bn1, bn2);
306 /* okay. is it one more? */
307 if (!BN_is_one(bn3)) {
313 /* Set these things up for the conv function */
314 rbuf[PASSWDLEN] = '\0';
317 err = AFPERR_NOTAUTH;
318 PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation,
320 if (PAM_error != PAM_SUCCESS)
323 /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */
324 pam_set_item(pamh, PAM_TTY, "afpd");
325 pam_set_item(pamh, PAM_RHOST, hostname);
326 PAM_error = pam_authenticate(pamh,0);
327 if (PAM_error != PAM_SUCCESS) {
328 if (PAM_error == PAM_MAXTRIES)
329 err = AFPERR_PWDEXPR;
333 PAM_error = pam_acct_mgmt(pamh, 0);
334 if (PAM_error != PAM_SUCCESS) {
335 if (PAM_error == PAM_ACCT_EXPIRED)
336 err = AFPERR_PWDEXPR;
337 #ifdef PAM_AUTHTOKEN_REQD
338 else if (PAM_error == PAM_AUTHTOKEN_REQD)
339 err = AFPERR_PWDCHNG;
344 #ifndef PAM_CRED_ESTABLISH
345 #define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
347 PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
348 if (PAM_error != PAM_SUCCESS)
351 PAM_error = pam_open_session(pamh, 0);
352 if (PAM_error != PAM_SUCCESS)
355 memset(rbuf, 0, PASSWDLEN); /* zero out the password */
360 pam_end(pamh, PAM_error);
362 memset(rbuf, 0, CRYPT2BUFLEN);
367 static void pam_logout() {
368 pam_close_session(pamh, 0);
374 /* change pw for dhx needs a couple passes to get everything all
375 * right. basically, it's like the login/logincont sequence */
376 static int pam_changepw(void *obj, char *username,
377 struct passwd *pwd, char *ibuf, int ibuflen,
378 char *rbuf, int *rbuflen)
380 BIGNUM *bn1, *bn2, *bn3;
389 memcpy(&sessid, ibuf, sizeof(sessid));
390 ibuf += sizeof(sessid);
392 if (!sessid) { /* no sessid -> initialization phase */
393 PAM_username = username;
394 ibuflen -= sizeof(sessid);
395 return dhx_setup(obj, ibuf, ibuflen, rbuf, rbuflen);
399 /* otherwise, it's like logincont but different. */
401 /* check out the session id */
402 if (sessid != dhxhash(obj))
405 /* we need this for pam */
406 if (uam_afpserver_option(obj, UAM_OPTION_HOSTNAME,
407 (void *) &hostname, NULL) < 0)
410 /* grab the client's nonce, old password, and new password. */
411 CAST_cbc_encrypt(ibuf, ibuf, CHANGEPWBUFLEN, &castkey,
412 msg3_iv, CAST_DECRYPT);
413 memset(&castkey, 0, sizeof(castkey));
415 /* check to make sure that the random number is the same. we
416 * get sent back an incremented random number. */
417 if (!(bn1 = BN_bin2bn(ibuf, KEYSIZE, NULL)))
420 if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
425 /* zero out the random number */
426 memset(rbuf, 0, sizeof(randbuf));
427 memset(randbuf, 0, sizeof(randbuf));
429 if (!(bn3 = BN_new())) {
435 BN_sub(bn3, bn1, bn2);
439 /* okay. is it one more? */
441 if (!BN_is_one(bn3)) {
448 /* Set these things up for the conv function. the old password
451 ibuf[PASSWDLEN + PASSWDLEN] = '\0';
452 PAM_password = ibuf + PASSWDLEN;
454 PAM_error = pam_start("netatalk", username, &PAM_conversation,
456 if (PAM_error != PAM_SUCCESS)
458 pam_set_item(lpamh, PAM_TTY, "afpd");
459 pam_set_item(lpamh, PAM_RHOST, hostname);
461 /* we might need to do this as root */
464 PAM_error = pam_authenticate(lpamh, 0);
465 if (PAM_error != PAM_SUCCESS) {
467 pam_end(lpamh, PAM_error);
468 return AFPERR_NOTAUTH;
471 /* clear out old passwd */
472 memset(ibuf + PASSWDLEN, 0, PASSWDLEN);
476 ibuf[PASSWDLEN] = '\0';
478 /* this really does need to be done as root */
479 PAM_error = pam_chauthtok(lpamh, 0);
480 seteuid(uid); /* un-root ourselves. */
481 memset(ibuf, 0, PASSWDLEN);
482 if (PAM_error != PAM_SUCCESS) {
483 pam_end(lpamh, PAM_error);
484 return AFPERR_ACCESS;
492 static int uam_setup(const char *path)
494 if (uam_register(UAM_SERVER_LOGIN, path, "DHCAST128", pam_login,
495 pam_logincont, pam_logout) < 0)
498 if (uam_register(UAM_SERVER_CHANGEPW, path, "DHCAST128",
500 uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
504 /*uam_register(UAM_SERVER_PRINTAUTH, path, "DHCAST128",
510 static void uam_cleanup(void)
512 uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
513 uam_unregister(UAM_SERVER_CHANGEPW, "DHCAST128");
514 /*uam_unregister(UAM_SERVER_PRINTAUTH, "DHCAST128"); */
517 UAM_MODULE_EXPORT struct uam_export uams_dhx = {
520 uam_setup, uam_cleanup
523 #endif /* USE_PAM && UAM_DHX */