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.
32 #include "choose_authen.h" /* for "struct authen_data" */
33 #include "do_author.h" /* for "struct identity" */
38 #include "default_fn.h"
42 static int do_sendauth_fn TAC_ARGS((struct authen_data *data));
43 static void outbound_chap TAC_ARGS((struct authen_data *data));
44 static void outbound_pap TAC_ARGS((struct authen_data *data));
47 static void outbound_mschap TAC_ARGS((struct authen_data *data));
51 int sendauth_fn TAC_ARGS((struct authen_data *data));
54 struct authen_data *data;
59 name = data->NAS_id->username;
61 if (STREQ(name, DEFAULT_USERNAME)) {
62 /* This username is only valid for authorization */
63 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
66 retval = do_sendauth_fn(data);
71 case TAC_PLUS_AUTHEN_TYPE_CHAP:
76 case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
81 case TAC_PLUS_AUTHEN_TYPE_PAP:
90 report(LOG_INFO, "%s-sendauth query for '%s' %s from %s %s",
92 name && name[0] ? name : "unknown",
93 session.peer, session.port,
94 (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
95 "accepted" : "rejected");
101 * For PAP we need to supply the outgoing PAP cleartext password.
102 * from the config file.
104 * For CHAP, we expect an id and a challenge. We will return an MD5 hash
105 * if we're successful,
107 * Return 0 if data->status is valid, otherwise 1
110 static int do_sendauth_fn TAC_ARGS((struct authen_data *data));
114 struct authen_data *data;
116 const char *name, *exp_date;
118 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
120 /* We must have a username */
121 if (!data->NAS_id->username[0]) {
122 /* Missing username is a gross error */
123 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
124 data->server_msg = tac_strdup("No username supplied");
125 report(LOG_ERR, "%s: No username for sendauth_fn", session.peer);
128 name = data->NAS_id->username;
130 switch (data->type) {
131 case TAC_PLUS_AUTHEN_TYPE_CHAP:
136 case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
137 outbound_mschap(data);
141 case TAC_PLUS_AUTHEN_TYPE_PAP:
146 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
147 report(LOG_ERR, "%s %s: %s Illegal data type for sendauth_fn",
148 session.peer, session.port, name);
152 exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);
153 set_expiration_status(exp_date, data);
157 static void outbound_pap TAC_ARGS((struct authen_data *data));
161 struct authen_data *data;
163 const char *secret, *p, *name;
165 name = data->NAS_id->username;
167 /* We must have a username */
169 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
173 /* Return her secret outbound PAP info */
174 secret = cfg_get_opap_secret(name, TAC_PLUS_RECURSE);
176 if (debug & DEBUG_AUTHEN_FLAG) {
177 report(LOG_ERR, "%s %s: No opap secret for %s",
178 session.peer, session.port, name);
180 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
184 p = tac_find_substring("cleartext ", secret);
186 /* Should never happen */
187 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
188 report(LOG_ERR, "%s %s: Illegal opap secret format %s",
189 session.peer, session.port, secret);
193 data->server_data = (unsigned char *) tac_strdup(p);
194 data->server_dlen = strlen((char *) data->server_data);
195 data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
198 static void outbound_chap TAC_ARGS((struct authen_data *data));
202 struct authen_data *data;
204 const char *name, *secret, *chal, *p;
205 char digest[MD5_LEN];
211 name = data->NAS_id->username;
214 report(LOG_ERR, "%s %s: no username for outbound_chap",
215 session.peer, session.port);
216 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
220 id = data->client_data[0];
222 chal_len = data->client_dlen - 1;
224 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
228 if (debug & DEBUG_AUTHEN_FLAG) {
229 report(LOG_DEBUG, "%s %s: user %s, id=%d chal_len=%d",
230 session.peer, session.port, name, (int)id, chal_len);
234 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
237 secret = cfg_get_chap_secret(name, TAC_PLUS_RECURSE);
239 /* If there is no chap password for this user, see if there is
240 a global password for her that we can use */
242 secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
246 /* No secret. Fail */
247 if (debug & DEBUG_AUTHEN_FLAG) {
248 report(LOG_DEBUG, "%s %s: No chap or global secret for %s",
249 session.peer, session.port, name);
251 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
256 p = tac_find_substring("cleartext ", secret);
258 /* Should never happen */
259 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
260 report(LOG_ERR, "%s %s: Illegal opap secret format %s",
261 session.peer, session.port, secret);
267 * We now have the secret, the id, and the challenge value.
268 * Put them all together, and run them through the MD5 digest
271 inlen = sizeof(u_char) + strlen(secret) + chal_len;
272 mdp = (u_char *)tac_malloc(inlen);
274 bcopy(secret, &mdp[1], strlen(secret));
275 chal = data->client_data + 1;
276 bcopy(chal, mdp + strlen(secret) + 1, chal_len);
278 MD5Update(&mdcontext, mdp, inlen);
279 MD5Final((u_char *)digest, &mdcontext);
283 * Now return the calculated response value */
285 data->server_data = tac_malloc(MD5_LEN);
286 bcopy(digest, data->server_data, MD5_LEN);
287 data->server_dlen = MD5_LEN;
289 data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
294 static void outbound_mschap TAC_ARGS((struct authen_data *data));
297 outbound_mschap(data)
298 struct authen_data *data;
300 const char *name, *secret, *chal, *p;
304 name = data->NAS_id->username;
307 report(LOG_ERR, "%s %s: no username for outbound_mschap",
308 session.peer, session.port);
309 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
313 id = data->client_data[0];
315 chal_len = data->client_dlen - 1;
316 if (data->client_dlen <= 2) {
317 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
321 if (debug & DEBUG_AUTHEN_FLAG) {
322 report(LOG_DEBUG, "%s %s: user %s, id=%d chal_len=%d",
323 session.peer, session.port, name, (int)id, chal_len);
327 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
330 secret = cfg_get_mschap_secret(name, TAC_PLUS_RECURSE);
332 /* If there is no chap password for this user, see if there is
333 a global password for her that we can use */
335 secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
339 /* No secret. Fail */
340 if (debug & DEBUG_AUTHEN_FLAG) {
341 report(LOG_DEBUG, "%s %s: No ms-chap or global secret for %s",
342 session.peer, session.port, name);
344 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
348 p = tac_find_substring("cleartext ", secret);
350 /* Should never happen */
351 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
352 report(LOG_ERR, "%s %s: Illegal ms-chap secret format %s",
353 session.peer, session.port, secret);
359 * We now have the secret, the id, and the challenge value.
360 * Put them all together, and run them through the MD4 digest
363 chal = data->client_data + 1;
366 * Now return the calculated response value */
368 data->server_data = tac_malloc(MSCHAP_DIGEST_LEN);
370 mschap_lmchallengeresponse(chal,secret,&data->server_data[0]);
371 mschap_ntchallengeresponse(chal,secret,&data->server_data[24]);
373 data->server_data[48] = 1; /* Mark it to use the NT response*/
374 data->server_dlen = MSCHAP_DIGEST_LEN;
376 data->status = TAC_PLUS_AUTHEN_STATUS_PASS;