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