2 Copyright (c) 1995-1998 by Cisco systems, Inc.
4 Permission to use, copy, modify, and distribute this software for
5 any purpose and without fee is hereby granted, provided that this
6 copyright and permission notice appear on all copies of the
7 software and supporting documentation, the name of Cisco Systems,
8 Inc. not be used in advertising or publicity pertaining to
9 distribution of the program without specific prior permission, and
10 notice be given in supporting documentation that modification,
11 copying and distribution is by permission of Cisco Systems, Inc.
13 Cisco Systems, Inc. makes no representations about the suitability
14 of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS
15 IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
16 WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 FITNESS FOR A PARTICULAR PURPOSE.
27 #include "default_fn.h"
34 #include "choose_authen.h" /* for "struct authen_data" */
35 #include "do_author.h" /* for "struct identity" */
44 #endif /* MSCHAP_DES */
54 static void chap_verify TAC_ARGS((struct authen_data *data));
55 static void arap_verify TAC_ARGS((struct authen_data *data));
56 static void pap_verify TAC_ARGS((struct authen_data *data));
57 static void tac_login TAC_ARGS((struct authen_data *data, struct private_data *p));
60 static void mschap_verify TAC_ARGS((struct authen_data *data));
64 /* internal state variables */
65 #define STATE_AUTHEN_START 0 /* no requests issued */
66 #define STATE_AUTHEN_GETUSER 1 /* username has been requested */
67 #define STATE_AUTHEN_GETPASS 2 /* password has been requested */
70 char password[MAX_PASSWD_LEN + 1];
76 * Default tacacs login authentication function. Wants a username
77 * and a password, and tries to verify them.
79 * Choose_authen will ensure that we already have a username before this
82 * We will query for a password and keep it in the method_data.
84 * Any strings returned via pointers in authen_data must come from the
85 * heap. They will get freed by the caller.
87 * Return 0 if data->status is valid, otherwise 1
90 int default_fn TAC_ARGS((struct authen_data *data));
94 struct authen_data *data;
96 struct private_data *p;
97 char *name = data->NAS_id->username;
99 p = (struct private_data *) data->method_data;
101 /* An abort has been received. Clean up and return */
102 if (data->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
103 if (data->method_data)
104 free(data->method_data);
105 data->method_data = NULL;
108 /* Initialise method_data if first time through */
110 p = (struct private_data *) tac_malloc(sizeof(struct private_data));
111 bzero(p, sizeof(struct private_data));
112 data->method_data = p;
113 p->state = STATE_AUTHEN_START;
115 if (STREQ(name, DEFAULT_USERNAME)) {
116 /* Never authenticate this user. It's for authorization only */
117 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
120 "authentication query for '%s' %s from %s rejected",
121 name && name[0] ? name : "unknown",
122 session.port, session.peer);
126 if (data->action != TAC_PLUS_AUTHEN_LOGIN) {
127 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
129 switch (data->type) {
130 case TAC_PLUS_AUTHEN_TYPE_CHAP:
131 /* set status inside chap_verify */
135 report(LOG_DEBUG, "chap-login query for '%s' %s from %s %s",
136 name && name[0] ? name : "unknown",
137 session.port, session.peer,
138 (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
139 "accepted" : "rejected");
144 case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
145 /* set status inside mschap_verify */
149 report(LOG_DEBUG, "mschap-login query for '%s' %s from %s %s",
150 name && name[0] ? name : "unknown",
151 session.port, session.peer,
152 (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
153 "accepted" : "rejected");
158 case TAC_PLUS_AUTHEN_TYPE_ARAP:
159 /* set status inside arap_verify */
163 report(LOG_DEBUG, "arap query for '%s' %s from %s %s",
164 name && name[0] ? name : "unknown",
165 session.port, session.peer,
166 (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
167 "accepted" : "rejected");
171 case TAC_PLUS_AUTHEN_TYPE_PAP:
175 report(LOG_INFO, "pap-login query for '%s' %s from %s %s",
176 name && name[0] ? name : "unknown",
179 (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
180 "accepted" : "rejected");
184 case TAC_PLUS_AUTHEN_TYPE_ASCII:
186 switch (data->status) {
187 case TAC_PLUS_AUTHEN_STATUS_GETPASS:
188 case TAC_PLUS_AUTHEN_STATUS_GETUSER:
189 case TAC_PLUS_AUTHEN_STATUS_GETDATA:
190 /* Authentication still in progress. More data required */
194 /* Authentication finished */
196 report(LOG_INFO, "login query for '%s' %s from %s %s",
197 name && name[0] ? name : "unknown",
200 (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
201 "accepted" : "rejected");
206 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
211 if (data->method_data)
212 free(data->method_data);
213 data->method_data = NULL;
215 switch (data->status) {
216 case TAC_PLUS_AUTHEN_STATUS_ERROR:
217 case TAC_PLUS_AUTHEN_STATUS_FAIL:
218 case TAC_PLUS_AUTHEN_STATUS_PASS:
222 report(LOG_ERR, "%s %s: default_fn set bogus status value %d",
223 session.peer, session.port, data->status);
224 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
229 /* Do a login requiring a username & password. We already know the
230 * username. We may return GETPASS to get a password if we need it.
231 * The password will be stored in the private data
235 static void tac_login TAC_ARGS((struct authen_data *data, struct private_data *p));
239 struct authen_data *data;
240 struct private_data *p;
245 name = data->NAS_id->username;
248 /* something awful has happened. Give up and die */
249 report(LOG_ERR, "%s %s: no username for login",
250 session.peer, session.port);
251 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
254 /* Do we have a password? */
255 passwd = p->password;
259 /* no password yet. Either we need to ask for one and expect to get
260 * called again when it's supplied, or we already asked for one and
261 * we should have a reply. */
264 case STATE_AUTHEN_GETPASS:
265 /* We already asked for a password. This should be the reply */
266 if (data->client_msg) {
267 pwlen = MIN((int) strlen(data->client_msg), MAX_PASSWD_LEN);
271 strncpy(passwd, data->client_msg, pwlen);
272 passwd[pwlen] = '\0';
275 case STATE_AUTHEN_START:
276 /* if we're at the username stage, and the user has
277 * nopasswd defined, then return a PASS
279 if (cfg_get_user_nopasswd(name, TAC_PLUS_RECURSE)) {
280 data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
285 data->flags = TAC_PLUS_AUTHEN_FLAG_NOECHO;
286 data->server_msg = tac_strdup("Password: ");
287 data->status = TAC_PLUS_AUTHEN_STATUS_GETPASS;
288 p->state = STATE_AUTHEN_GETPASS;
292 /* Now we have a username and password. Try validating */
294 /* Assume the worst */
295 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
296 verify(name, passwd, data, TAC_PLUS_RECURSE);
301 * Process an inbound PAP login. The username & password should be in
305 static void pap_verify TAC_ARGS((struct authen_data *data));
309 struct authen_data *data;
313 name = data->NAS_id->username;
316 /* something awful has happened. Give up and die */
317 report(LOG_ERR, "%s %s: no username for inbound PAP login",
318 session.peer, session.port);
319 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
322 /* get the password */
323 passwd = tac_malloc(data->client_dlen + 1);
324 bcopy(data->client_data, passwd, data->client_dlen);
325 passwd[data->client_dlen] = '\0';
327 /* Assume the worst */
328 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
329 verify(name, passwd, data, TAC_PLUS_RECURSE);
334 static void chap_verify TAC_ARGS((struct authen_data *data));
336 /* Verify the challenge and id against the response by looking up the
337 * chap secret in the config file. Set data->status appropriately.
341 struct authen_data *data;
343 char *name, *chal, digest[MD5_LEN];
345 const char *exp_date, *p;
351 if (!(char) data->NAS_id->username[0]) {
352 report(LOG_ERR, "%s %s: no username for chap_verify",
353 session.peer, session.port);
354 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
357 name = data->NAS_id->username;
359 id = data->client_data[0];
361 chal_len = data->client_dlen - 1 - MD5_LEN;
363 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
366 if (debug & DEBUG_AUTHEN_FLAG) {
367 report(LOG_DEBUG, "%s %s: chap user=%s, id=%d chal_len=%d",
368 session.peer, session.port, name, (int) id, chal_len);
370 /* report_hex(LOG_DEBUG, (u_char *)data->client_data + 1, chal_len); */
373 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
376 secret = cfg_get_chap_secret(name, TAC_PLUS_RECURSE);
378 /* If there is no chap password for this user, see if there is a global
379 * password for her that we can use */
381 secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
384 /* No secret. Fail */
385 if (debug & DEBUG_AUTHEN_FLAG) {
386 report(LOG_DEBUG, "%s %s: No chap or global secret for %s",
387 session.peer, session.port, name);
389 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
392 p = tac_find_substring("cleartext ", secret);
394 report(LOG_ERR, "%s %s: %s chap secret %s is not cleartext",
395 session.peer, session.port, name, secret);
396 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
401 /* We now have the secret, the id, and the challenge value. Put them all
402 * together, and run them through the MD5 digest algorithm. */
404 inlen = sizeof(u_char) + strlen(secret) + chal_len;
405 mdp = (u_char *) tac_malloc(inlen);
407 bcopy(secret, &mdp[1], strlen(secret));
408 chal = data->client_data + 1;
409 bcopy(chal, mdp + strlen(secret) + 1, chal_len);
411 MD5Update(&mdcontext, mdp, inlen);
412 MD5Final((u_char *) digest, &mdcontext);
415 /* Now compare the received response value with the just calculated
416 * digest value. If they are equal, it's a pass, otherwise it's a
419 if (bcmp(digest, data->client_data + 1 + chal_len, MD5_LEN)) {
420 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
422 data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
425 exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);
426 set_expiration_status(exp_date, data);
430 static void pw_bitshift TAC_ARGS((char *pw));
433 * Force the "parity" bit to zero on a password before passing it to
434 * des. This is not documented anywhere. (I believe forcing the parity
435 * to zero reduces the integrity of the encrypted keys but this is
436 * what Apple chose to do).
443 unsigned char pws[8];
445 /* key is 0 padded */
446 for (i = 0; i < 8; i++)
449 /* parity bit is always zero (this seem bogus) */
450 for (i = 0; i < 8 && pw[i]; i++)
457 static void arap_verify TAC_ARGS((struct authen_data *data));
461 struct authen_data *data;
463 char nas_chal[8], r_chal[8], r_resp[8], secret[8];
464 const char *name, *cfg_secret, *exp_date, *p;
466 if (!(char) data->NAS_id->username[0]) {
467 report(LOG_ERR, "%s %s: no username for arap_verify",
468 session.peer, session.port);
469 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
472 name = data->NAS_id->username;
474 bcopy(data->client_data, nas_chal, 8);
475 bcopy(data->client_data + 8, r_chal, 8);
476 bcopy(data->client_data + 8 + 8, r_resp, 8);
479 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
482 cfg_secret = cfg_get_arap_secret(name, TAC_PLUS_RECURSE);
484 /* If there is no arap password for this user, see if there is a global
485 * password for her that we can use */
487 cfg_secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
490 /* No secret. Fail */
491 if (debug & DEBUG_AUTHEN_FLAG) {
492 report(LOG_DEBUG, "%s %s: No arap or global secret for %s",
493 session.peer, session.port, name);
495 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
498 p = tac_find_substring("cleartext ", cfg_secret);
500 report(LOG_ERR, "%s %s: %s arap secret %s is not cleartext",
501 session.peer, session.port, name, cfg_secret);
502 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
505 /* need to allocate 8 bytes for secret, even if it's actually shorter */
506 bzero(secret, sizeof(secret));
516 #endif /* ARAP_DES */
518 /* Now compare the remote's response value with the just calculated one
519 * value. If they are equal, it's a pass, otherwise it's a failure */
521 if (bcmp(nas_chal, r_resp, 8)) {
522 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
524 data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
528 /* Now calculate the response to the remote's challenge */
533 #endif /* ARAP_DES */
535 data->server_data = tac_malloc(8);
536 data->server_dlen = 8;
537 bcopy(r_chal, data->server_data, 8);
539 exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);
540 set_expiration_status(exp_date, data);
546 /* Following code is added for ms-chap */
548 static void mschap_desencrypt TAC_ARGS((const char *clear, unsigned char *str, unsigned char *cypher));
551 mschap_desencrypt(clear, str, cypher)
554 unsigned char *cypher;
556 unsigned char key[8];
558 /* des_state_type *des_state = NULL; */
562 /* Copy the key inserting parity bits */
565 /* This method makes it obvious what we are doing */
567 #define getbit(bit,array) ((array[bit/8] & (1 << (7-(bit%8)))) !=0)
568 #define setbit(bit,array) (array[bit/8] |= (1 << (7-(bit%8))))
574 for (i = 0; i < 56; i++) {
575 if (i && (i % 7 == 0)) {
584 /* this is a little more cryptic, but faster basicly we are insering a
585 * bit into the stream after every 7 bits */
587 key[0] = ((str[0] & 0xfe));
588 key[1] = ((str[0] & 0x01) << 7) | ((str[1] & 0x0fc) >> 1);
589 key[2] = ((str[1] & 0x03) << 6) | ((str[2] & 0x0f8) >> 2);
590 key[3] = ((str[2] & 0x07) << 5) | ((str[3] & 0x0f0) >> 3);
591 key[4] = ((str[3] & 0x0f) << 4) | ((str[4] & 0x0e0) >> 4);
592 key[5] = ((str[4] & 0x1f) << 3) | ((str[5] & 0x0c0) >> 5);
593 key[6] = ((str[5] & 0x3f) << 2) | ((str[6] & 0x080) >> 6);
594 key[7] = ((str[6] & 0x7f) << 1);
598 /* copy clear to cypher, cause our des encrypts in place */
599 memcpy(cypher, clear, 8);
601 des_init(0,&des_state);
602 des_setkey(des_state,key);
603 des_endes(des_state,cypher);
611 #endif /* MSCHAP_DES */
615 static void mschap_deshash TAC_ARGS((unsigned char *clear, unsigned char *cypher));
618 mschap_deshash(clear, cypher)
619 unsigned char *clear;
620 unsigned char *cypher;
622 mschap_desencrypt(MSCHAP_KEY, clear, cypher);
626 static void mschap_lmpasswordhash TAC_ARGS((const char *password, unsigned char *passwordhash));
629 mschap_lmpasswordhash(password, passwordhash)
630 const char *password;
631 unsigned char *passwordhash;
633 unsigned char upassword[15];
636 memset(upassword, 0, 15);
637 while (password[i]) {
638 upassword[i] = toupper(password[i]);
642 mschap_deshash(&upassword[0], &passwordhash[0]);
643 mschap_deshash(&upassword[7], &passwordhash[8]);
647 static void mschap_challengeresponse TAC_ARGS((const char *challenge, unsigned char *passwordhash, unsigned char *response));
650 mschap_challengeresponse(challenge, passwordhash, response)
651 const char *challenge;
652 unsigned char *passwordhash;
653 unsigned char *response;
655 unsigned char zpasswordhash[21];
657 memset(zpasswordhash, 0, 21);
658 memcpy(zpasswordhash, passwordhash, 16);
660 mschap_desencrypt(challenge, &zpasswordhash[0], &response[0]);
661 mschap_desencrypt(challenge, &zpasswordhash[7], &response[8]);
662 mschap_desencrypt(challenge, &zpasswordhash[14], &response[16]);
666 void mschap_lmchallengeresponse TAC_ARGS((const char *challenge, const char *password, unsigned char *response));
669 mschap_lmchallengeresponse(challenge, password, response)
670 const char *challenge;
671 const char *password;
672 unsigned char *response;
674 unsigned char passwordhash[16];
676 mschap_lmpasswordhash(password, passwordhash);
677 mschap_challengeresponse(challenge, passwordhash, response);
681 static int mschap_unicode_len TAC_ARGS((unsigned char *password));
684 mschap_unicode_len(password)
685 unsigned char *password;
690 while ((password[i] || password[i + 1]) && (i < 512)) {
698 static void mschap_ntpasswordhash TAC_ARGS((const char *password, unsigned char *passwordhash));
701 mschap_ntpasswordhash(password, passwordhash)
702 const char *password;
703 unsigned char *passwordhash;
708 unsigned char unicode_password[512];
710 memset(unicode_password, 0, 512);
713 memset(unicode_password, 0, 512);
716 unicode_password[i++] = *cp++;
717 unicode_password[i++] = '\0';
721 MD4Update(&context, unicode_password,
722 mschap_unicode_len(unicode_password));
723 MD4Final(passwordhash, &context);
727 void mschap_ntchallengeresponse TAC_ARGS((const char *challenge, const char *password, unsigned char *response));
730 mschap_ntchallengeresponse(challenge, password, response)
731 const char *challenge;
732 const char *password;
733 unsigned char *response;
735 unsigned char passwordhash[16];
737 mschap_ntpasswordhash(password, passwordhash);
738 mschap_challengeresponse(challenge, passwordhash, response);
742 /* Verify the challenge and id against the response by looking up the
743 * ms-chap secret in the config file. Set data->status appropriately.
746 static void mschap_verify TAC_ARGS((struct authen_data *data));
750 struct authen_data *data;
752 const char *name, *secret, *chal, *resp;
753 const char *exp_date, *p;
756 unsigned char lmresponse[24];
757 unsigned char ntresponse[24];
760 if (!(char) data->NAS_id->username[0]) {
761 report(LOG_ERR, "%s %s: no username for mschap_verify",
762 session.peer, session.port);
763 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
766 name = data->NAS_id->username;
768 id = data->client_data[0];
770 chal_len = data->client_dlen - 1 - MSCHAP_DIGEST_LEN;
771 if (data->client_dlen <= (MSCHAP_DIGEST_LEN + 2)) {
772 /* Invalid packet or NULL challenge */
773 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
776 if (debug & DEBUG_AUTHEN_FLAG) {
777 report(LOG_DEBUG, "%s %s: ms-chap user=%s, id=%d chal_len=%d",
778 session.peer, session.port, name, (int) id, chal_len);
780 /* report_hex(LOG_DEBUG, (u_char *)data->client_data + 1, chal_len); */
783 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
786 secret = cfg_get_mschap_secret(name, TAC_PLUS_RECURSE);
788 /* If there is no ms-chap password for this user, see if there is a
789 * global password for her that we can use */
791 secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
794 /* No secret. Fail */
795 if (debug & DEBUG_AUTHEN_FLAG) {
796 report(LOG_DEBUG, "%s %s: No ms-chap or global secret for %s",
797 session.peer, session.port, name);
799 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
802 p = tac_find_substring("cleartext ", secret);
804 report(LOG_ERR, "%s %s: %s ms-chap secret %s is not cleartext",
805 session.peer, session.port, name, secret);
806 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
811 /* We now have the secret, the id, and the challenge value. Put them all
812 * together, and run them through the MD4 digest algorithm. */
813 chal = data->client_data + 1;
814 resp = data->client_data + 1 + chal_len;
816 mschap_lmchallengeresponse(chal, secret, lmresponse);
817 mschap_ntchallengeresponse(chal, secret, ntresponse);
819 /* Now compare the received response value with the just calculated
820 * digest value. If they are equal, it's a pass, otherwise it's a
823 bcmp_status = bcmp(ntresponse, &resp[24], 24);
825 bcmp_status = bcmp(lmresponse, &resp[0], 24);
828 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
830 data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
833 exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);
834 set_expiration_status(exp_date, data);