Import of tac_plus.v8.tar.gz: 173206 bytes, md5:
[tac_plus.git] / authen.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
22 static int choose();
23 static void authenticate();
24 static void do_start();
25
26 /*
27  *  Come here when we receive an authentication START packet
28  */
29
30 void
31 authen(pak)
32 u_char *pak;
33 {
34     char msg[55];
35     struct authen_start *start;
36     HDR *hdr;
37
38     hdr = (HDR *) pak;
39     start = (struct authen_start *) (pak + TAC_PLUS_HDR_SIZE);
40
41     if ((hdr->seq_no != 1) ||
42         (ntohl(hdr->datalength) != TAC_AUTHEN_START_FIXED_FIELDS_SIZE + 
43          start->user_len + start->port_len + start->rem_addr_len +
44          start->data_len)) {
45         send_authen_error("Invalid AUTHEN/START packet (check keys)");
46         return;
47     }
48
49     switch (start->action) {
50     case TAC_PLUS_AUTHEN_LOGIN:
51     case TAC_PLUS_AUTHEN_SENDAUTH:
52     case TAC_PLUS_AUTHEN_SENDPASS:
53         do_start(pak);
54         return;
55
56     default:
57         sprintf(msg, "Invalid AUTHEN/START action=%d", start->action);
58         send_authen_error(msg);
59         return;
60     }
61 }
62
63 /*
64  * We have a valid AUTHEN/START packet. Fill out data structures and
65  * attempt to authenticate.
66  */
67
68 static void
69 do_start(pak)
70 u_char *pak;
71 {
72     struct identity identity;
73     struct authen_data authen_data;
74     struct authen_type authen_type;
75     struct authen_start *start;
76     u_char *p;
77     int ret;
78
79     if (debug & DEBUG_PACKET_FLAG)
80         report(LOG_DEBUG, "Authen Start request");
81
82     /* fixed fields of this packet */
83     start = (struct authen_start *) (pak + TAC_PLUS_HDR_SIZE);
84
85     /* variable length data starts here */
86     p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_START_FIXED_FIELDS_SIZE;
87
88     /* The identity structure */
89
90     /* zero out identity struct so that all strings can be NULL terminated */
91     bzero(&identity, sizeof(struct identity));
92
93     identity.username = tac_make_string(p, (int)start->user_len);
94     p += start->user_len;
95
96     identity.NAS_name = tac_strdup(session.peer);
97
98     identity.NAS_port = tac_make_string(p, (int)start->port_len);
99     p += start->port_len;
100
101     if (start->port_len <= 0) {
102         strcpy(session.port, "unknown-port");
103     } else {
104         strcpy(session.port, identity.NAS_port);
105     }
106
107     identity.NAC_address = tac_make_string(p, (int)start->rem_addr_len);
108     p += start->rem_addr_len;
109
110     identity.priv_lvl = start->priv_lvl;
111
112     /* The authen_data structure */
113
114     bzero(&authen_data, sizeof(struct authen_data));
115
116     authen_data.NAS_id      = &identity;
117     authen_data.action      = start->action;
118     authen_data.service     = start->service;
119     authen_data.type        = start->authen_type;
120     authen_data.client_dlen = start->data_len;
121
122     authen_data.client_data = tac_malloc(start->data_len);
123     bcopy(p, authen_data.client_data, start->data_len);
124
125     /* The authen_type structure */
126
127     bzero(&authen_type, sizeof(struct authen_type));
128
129     authen_type.authen_type = start->authen_type;
130
131     /* All data structures are now initialised. Now see if we can
132      * authenticate this puppy. Begin by choosing a suitable
133      * authentication function to call to actually do the work. */
134
135 #ifdef TCPWRAPPER
136 if (check_from_wrap(&identity)) {   
137 #endif
138     ret = choose(&authen_data, &authen_type);
139
140     switch (ret) {
141     case 1:
142         /* A successful choice. Authenticate */
143         authenticate(&authen_data, &authen_type);
144         break;
145
146     case 0:
147         /* We lost our connection, aborted, or something dreadful happened */
148         break;
149     }
150 #ifdef TCPWRAPPER
151 } else {
152 send_authen_error("You are not allowed to access here");
153 }
154 #endif
155     /* free data structures */
156     if (authen_data.server_msg) {
157         free(authen_data.server_msg);
158         authen_data.server_msg = NULL;
159     }
160     if (authen_data.server_data) {
161         free(authen_data.server_data);
162         authen_data.server_data = NULL;
163     }
164     if (authen_data.client_msg) {
165         free(authen_data.client_msg);
166         authen_data.client_msg = NULL;
167     }
168     if (authen_data.client_data) {
169         free(authen_data.client_data);
170         authen_data.client_data = NULL;
171     }
172     if (authen_data.method_data) {
173         report(LOG_ERR, 
174                "%s: Method data not set to NULL after authentication",
175                session.peer);
176     }
177     free(identity.username);
178     free(identity.NAS_name);
179     free(identity.NAS_port);
180     free(identity.NAC_address);
181     return;
182 }
183
184 /* Choose an authentication function. Return 1 if we successfully
185    chose a function.  0 if we couldn't make a choice for some reason */
186
187 static int 
188 choose(datap, typep)
189 struct authen_data *datap;
190 struct authen_type *typep;
191 {
192     int iterations = 0;
193     int status;
194     char *prompt;
195     struct authen_cont *cont;
196     u_char *reply;
197     u_char *p;
198     struct identity *identp;
199
200     while (1) {
201
202         /* check interation counter here */
203
204         if (++iterations >= TAC_PLUS_MAX_ITERATIONS) {
205             report(LOG_ERR, "%s: %s Too many iterations for choose_authen",
206                    session.peer, 
207                    session.port);
208             return (0);
209         }
210         status = choose_authen(datap, typep);
211
212         if (status && (debug & DEBUG_PACKET_FLAG))
213             report(LOG_DEBUG, "choose_authen returns %d", status);
214
215         switch (status) {
216
217         case CHOOSE_BADTYPE: /* FIXME */
218         default:
219             send_authen_error("choose_authen: unexpected failure return");
220             return (0);
221
222         case CHOOSE_OK:
223             if (debug & DEBUG_PACKET_FLAG)
224                 report(LOG_DEBUG, "choose_authen chose %s", typep->authen_name);
225             return (1);
226
227         case CHOOSE_FAILED:
228             send_authen_error("choose_authen: unacceptable authen method");
229             return (0);
230
231         case CHOOSE_GETUSER:
232             /* respond with GETUSER containing an optional message from
233              * authen_data.server_msg. */
234
235             datap->status = TAC_PLUS_AUTHEN_STATUS_GETUSER;
236             if (datap->service == TAC_PLUS_AUTHEN_SVC_LOGIN) {
237                 prompt = "\nUser Access Verification\n\nUsername: ";
238             } else {
239                 prompt = "Username: ";
240             }
241             send_authen_reply(TAC_PLUS_AUTHEN_STATUS_GETUSER, /* status */
242                               prompt, /* msg */
243                               strlen(prompt), /* msg_len */
244                               datap->server_data,
245                               datap->server_dlen,
246                               0 /* flags */);
247
248             if (datap->server_data) {
249                 free(datap->server_data);
250                 datap->server_dlen = 0;
251             }
252             /* expect a CONT from the NAS */
253             reply = get_authen_continue();
254             if (reply == NULL) {
255                 /* Typically premature close of connection */
256                 report(LOG_ERR, "%s %s: Null reply packet, expecting CONTINUE",
257                        session.peer, session.port);
258                 return (0);
259             }
260
261             cont = (struct authen_cont *) (reply + TAC_PLUS_HDR_SIZE);
262
263             if (cont->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
264                 char buf[65537];
265                 buf[0] = '\0';
266                 session.aborted = 1;
267
268                 if (cont->user_data_len) {
269                     /* An abort message exists. Log it */
270                     p = reply + TAC_PLUS_HDR_SIZE + 
271                         TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE + cont->user_msg_len;
272
273                     bcopy(p, buf, cont->user_data_len);
274                     buf[cont->user_data_len] = '\0';
275                 }
276                 report(LOG_INFO, "%s %s: Login aborted by request -- msg: %s", 
277                        session.peer, session.port, buf);
278                 free(reply);
279                 return(0);
280             }
281
282             p = reply + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE;
283
284             identp = datap->NAS_id;
285
286             if (identp->username) {
287                 free(identp->username);
288             }
289             identp->username = tac_make_string(p, cont->user_msg_len);
290             free(reply);
291         }
292     }
293     /* NOTREACHED */
294 }
295
296 /* Perform authentication assuming we have successfully chosen an
297    authentication method */
298 static void
299 authenticate(datap, typep)
300 struct authen_data *datap;
301 struct authen_type *typep;
302 {
303     int iterations = 0;
304     u_char *reply, *p;
305     struct authen_cont *cont;
306     int (*func) ();
307
308     if (debug & DEBUG_PACKET_FLAG)
309         report(LOG_DEBUG, "Calling authentication function");
310
311     func = typep->authen_func;
312
313     if (!func) {
314         send_authen_error("authenticate: cannot find function pointer");
315         return;
316     }
317
318     while (1) {
319         if (session.aborted)
320             return;
321
322         if (++iterations >= TAC_PLUS_MAX_ITERATIONS) {
323             send_authen_error("Too many iterations while authenticating");
324             return;
325         }
326
327         if ((*func) (datap)) {
328             send_authen_error("Unexpected authentication function failure");
329             return;
330         }
331
332         switch (datap->status) {
333
334         default:
335             send_authen_error("Illegal status value from authentication function");
336             return;
337
338         case TAC_PLUS_AUTHEN_STATUS_PASS:
339             /* A successful authentication */
340             send_authen_reply(TAC_PLUS_AUTHEN_STATUS_PASS,
341                               datap->server_msg,
342                               datap->server_msg ? strlen(datap->server_msg) : 0,
343                               datap->server_data,
344                               datap->server_dlen,
345                               0);
346             return;
347
348         case TAC_PLUS_AUTHEN_STATUS_ERROR:
349             /* never supposed to happen. reply with a server_msg if any, and
350              * bail out */
351             send_authen_error(datap->server_msg ? datap->server_msg :
352                             "authentication function: unspecified failure");
353             return;
354
355         case TAC_PLUS_AUTHEN_STATUS_FAIL:
356
357             /* An invalid user/password combination */
358             send_authen_reply(TAC_PLUS_AUTHEN_STATUS_FAIL,
359                               datap->server_msg,
360                               datap->server_msg ? strlen(datap->server_msg) : 0,
361                               NULL,
362                               0,
363                               0);
364             return;
365
366         case TAC_PLUS_AUTHEN_STATUS_GETUSER:
367         case TAC_PLUS_AUTHEN_STATUS_GETPASS:
368         case TAC_PLUS_AUTHEN_STATUS_GETDATA:
369
370             /* ship GETPASS/GETDATA containing
371              * datap->server_msg to NAS. */
372
373             send_authen_reply(datap->status,
374                               datap->server_msg,
375                               datap->server_msg ? strlen(datap->server_msg) : 0,
376                               datap->server_data,
377                               datap->server_dlen,
378                               datap->flags);
379
380             datap->flags = 0;
381
382             if (datap->server_msg) {
383                 free(datap->server_msg);
384                 datap->server_msg = NULL;
385             }
386             if (datap->server_data) {
387                 free(datap->server_data);
388                 datap->server_data = NULL;
389             }
390             if (datap->client_msg) {
391                 free(datap->client_msg);
392                 datap->client_msg = NULL;
393             }
394             reply = get_authen_continue();
395             if (!reply) {
396
397                 /* Typically due to a premature connection close */
398                 report(LOG_ERR, "%s %s: Null reply packet, expecting CONTINUE",
399                        session.peer, session.port);
400
401                 /* Tell the authentication function it should clean up
402                    any private data */
403
404                 datap->flags |= TAC_PLUS_CONTINUE_FLAG_ABORT;
405
406                 if (datap->method_data)
407                     ((*func) (datap));
408
409                 datap->flags = 0;
410                 return;
411             }
412
413             cont = (struct authen_cont *) (reply + TAC_PLUS_HDR_SIZE);
414
415             if (cont->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
416
417                 session.aborted = 1;
418
419                 /* Tell the authentication function to clean up
420                    its private data, if there is any */
421
422                 datap->flags |= TAC_PLUS_CONTINUE_FLAG_ABORT;
423                 if (datap->method_data)
424                     ((*func) (datap));
425                 datap->flags = 0;
426
427                 if (cont->user_data_len) {
428
429                     /* An abort message exists. Create a
430                        null-terminated string for authen_data */
431
432                     datap->client_data = (char *) 
433                         tac_malloc(cont->user_data_len + 1);
434
435                     p = reply + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE +
436                         cont->user_msg_len;
437
438                     bcopy(p, datap->client_data, cont->user_data_len);
439                     datap->client_data[cont->user_data_len] = '\0';
440                 }
441
442                 free(reply);
443                 return;
444             }
445
446             p = reply + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE;
447
448             switch (datap->status) {
449
450             case TAC_PLUS_AUTHEN_STATUS_GETDATA:
451             case TAC_PLUS_AUTHEN_STATUS_GETPASS:
452                 /* A response to our GETDATA/GETPASS request. Create a
453                  * null-terminated string for authen_data */
454                 datap->client_msg = (char *) tac_malloc(cont->user_msg_len + 1);
455                 bcopy(p, datap->client_msg, cont->user_msg_len);
456                 datap->client_msg[cont->user_msg_len] = '\0';
457                 free(reply);
458                 continue;
459
460             case TAC_PLUS_AUTHEN_STATUS_GETUSER:
461             default:
462                 report(LOG_ERR, "%s: authenticate: cannot happen",
463                        session.peer);
464                 send_authen_error("authenticate: cannot happen");
465                 free(reply);
466                 return;
467             }
468         }
469         /* NOTREACHED */
470     }
471 }
472