Release bumped to "gts4".
[tac_plus.git] / sendauth.c
1 /*
2    Copyright (c) 1995-1998 by Cisco systems, Inc.
3
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.
12
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.
18 */
19
20
21 #include "tac_plus.h"
22
23 #include <stdlib.h>
24
25 #include "sendauth.h"
26 #include "expire.h"
27 #include "md5.h"
28 #include "report.h"
29 #include "cfgfile.h"
30 #include "utils.h"
31 #include "pwlib.h"
32 #include "choose_authen.h"              /* for "struct authen_data" */
33 #include "do_author.h"                  /* for "struct identity" */
34 #include "packet.h"
35 #include "main.h"
36
37 #ifdef MSCHAP
38 #include "default_fn.h"
39 #endif
40
41
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));
45
46 #ifdef MSCHAP
47 static void outbound_mschap TAC_ARGS((struct authen_data *data));
48 #endif
49
50
51 int sendauth_fn TAC_ARGS((struct authen_data *data));
52
53 int sendauth_fn(data)
54 struct authen_data *data;
55 {
56     int retval;
57     char *name, *p;
58
59     name = data->NAS_id->username;
60
61     if (STREQ(name, DEFAULT_USERNAME)) {
62         /* This username is only valid for authorization */
63         data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
64         retval = 0;
65     } else {
66         retval = do_sendauth_fn(data);
67     }
68
69     if (debug) {
70         switch (data->type) {
71         case TAC_PLUS_AUTHEN_TYPE_CHAP:
72             p = "chap";
73             break;
74
75 #ifdef MSCHAP
76         case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
77             p = "ms-chap";
78             break;
79 #endif /* MSCHAP */
80
81         case TAC_PLUS_AUTHEN_TYPE_PAP:
82             p = "pap";
83             break;
84
85         default:
86             p = "unknown";
87             break;
88         }
89
90         report(LOG_INFO, "%s-sendauth query for '%s' %s from %s %s",
91                p,
92                name && name[0] ? name : "unknown",
93                session.peer, session.port,
94                (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
95                "accepted" : "rejected");
96     }
97     return (retval);
98 }
99
100 /*
101  * For PAP we need to supply the outgoing PAP cleartext password.
102  * from the config file.
103  *
104  * For CHAP, we expect an id and a challenge. We will return an MD5 hash
105  * if we're successful,
106  *
107  * Return 0 if data->status is valid, otherwise 1
108  */
109
110 static int do_sendauth_fn TAC_ARGS((struct authen_data *data));
111
112 static int
113 do_sendauth_fn(data)
114 struct authen_data *data;
115 {
116     const char *name, *exp_date;
117
118     data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
119
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);
126         return (0);
127     }
128     name = data->NAS_id->username;
129
130     switch (data->type) {
131     case TAC_PLUS_AUTHEN_TYPE_CHAP:
132         outbound_chap(data);
133         break;
134
135 #ifdef MSCHAP
136     case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
137         outbound_mschap(data);
138         break;
139 #endif /* MSCHAP */
140
141     case TAC_PLUS_AUTHEN_TYPE_PAP:
142         outbound_pap(data);
143         break;
144
145     default:
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);
149         return (0);
150     }
151
152     exp_date = cfg_get_expires(name, TAC_PLUS_RECURSE);
153     set_expiration_status(exp_date, data);
154     return (0);
155 }
156
157 static void outbound_pap TAC_ARGS((struct authen_data *data));
158
159 static void
160 outbound_pap(data)
161 struct authen_data *data;
162 {
163     const char *secret, *p, *name;
164
165     name = data->NAS_id->username;
166
167     /* We must have a username */
168     if (!name) {
169         data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
170         return;
171     }
172
173     /* Return her secret outbound PAP info */
174     secret = cfg_get_opap_secret(name, TAC_PLUS_RECURSE);
175     if (!secret) {
176         if (debug & DEBUG_AUTHEN_FLAG) {
177             report(LOG_ERR, "%s %s: No opap secret for %s",
178                    session.peer, session.port, name);
179         }
180         data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
181         return;
182     }
183
184     p = tac_find_substring("cleartext ", secret);
185     if (!p) {
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);
190         return;
191     }
192
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;
196 }
197
198 static void outbound_chap TAC_ARGS((struct authen_data *data));
199
200 static void
201 outbound_chap(data)
202 struct authen_data *data;
203 {
204     const char *name, *secret, *chal, *p;
205     char digest[MD5_LEN];
206     u_char *mdp;
207     char id;
208     int chal_len, inlen;
209     MD5_CTX mdcontext;
210
211     name = data->NAS_id->username;
212
213     if (!name) {
214         report(LOG_ERR, "%s %s: no username for outbound_chap",
215                session.peer, session.port);
216         data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
217         return;
218     }
219
220     id = data->client_data[0];
221
222     chal_len = data->client_dlen - 1;
223     if (chal_len < 0) {
224         data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
225         return;
226     }
227
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);
231     }
232
233     /* Assume failure */
234     data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
235
236     /* Get the secret */
237     secret = cfg_get_chap_secret(name, TAC_PLUS_RECURSE);
238
239     /* If there is no chap password for this user, see if there is
240        a global password for her that we can use */
241     if (!secret) {
242         secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
243     }
244
245     if (!secret) {
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);
250         }
251         data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
252         return;
253     }
254
255
256     p = tac_find_substring("cleartext ", secret);
257     if (!p) {
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);
262         return;
263     }
264     secret = p;
265
266     /*
267      * We now have the secret, the id, and the challenge value.
268      * Put them all together, and run them through the MD5 digest
269      * algorithm. */
270
271     inlen = sizeof(u_char) + strlen(secret) + chal_len;
272     mdp = (u_char *)tac_malloc(inlen);
273     mdp[0] = id;
274     bcopy(secret, &mdp[1], strlen(secret));
275     chal = data->client_data + 1;
276     bcopy(chal, mdp + strlen(secret) + 1, chal_len);
277     MD5Init(&mdcontext);
278     MD5Update(&mdcontext, mdp, inlen);
279     MD5Final((u_char *)digest, &mdcontext);
280     free(mdp);
281
282     /*
283      * Now return the calculated response value */
284
285     data->server_data = tac_malloc(MD5_LEN);
286     bcopy(digest, data->server_data, MD5_LEN);
287     data->server_dlen = MD5_LEN;
288
289     data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
290 }
291
292 #ifdef MSCHAP
293
294 static void outbound_mschap TAC_ARGS((struct authen_data *data));
295
296 static void
297 outbound_mschap(data)
298 struct authen_data *data;
299 {
300     const char *name, *secret, *chal, *p;
301     char id;
302     int chal_len;
303
304     name = data->NAS_id->username;
305
306     if (!name) {
307         report(LOG_ERR, "%s %s: no username for outbound_mschap",
308                session.peer, session.port);
309         data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
310         return;
311     }
312
313     id = data->client_data[0];
314
315     chal_len = data->client_dlen - 1;
316     if (data->client_dlen <= 2) {
317         data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
318         return;
319     }
320
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);
324     }
325
326     /* Assume failure */
327     data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
328
329     /* Get the secret */
330     secret = cfg_get_mschap_secret(name, TAC_PLUS_RECURSE);
331
332     /* If there is no chap password for this user, see if there is
333        a global password for her that we can use */
334     if (!secret) {
335         secret = cfg_get_global_secret(name, TAC_PLUS_RECURSE);
336     }
337
338     if (!secret) {
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);
343         }
344         data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
345         return;
346     }
347
348     p = tac_find_substring("cleartext ", secret);
349     if (!p) {
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);
354         return;
355     }
356     secret = p;
357
358     /*
359      * We now have the secret, the id, and the challenge value.
360      * Put them all together, and run them through the MD4 digest
361      * algorithm. */
362
363     chal = data->client_data + 1;
364
365     /*
366      * Now return the calculated response value */
367
368     data->server_data = tac_malloc(MSCHAP_DIGEST_LEN);
369
370     mschap_lmchallengeresponse(chal,secret,&data->server_data[0]);
371     mschap_ntchallengeresponse(chal,secret,&data->server_data[24]);
372
373     data->server_data[48] = 1; /* Mark it to use the NT response*/
374     data->server_dlen = MSCHAP_DIGEST_LEN;
375
376     data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
377 }
378
379 #endif /* MSCHAP */