Import of tac_plus.v8.tar.gz: 173206 bytes, md5:
[tac_plus.git] / skey_fn.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 #ifdef SKEY
21 #include "tac_plus.h"
22 #include "expire.h"
23
24 /* internal state variables */
25 #define STATE_AUTHEN_START   0  /* no requests issued */
26 #define STATE_AUTHEN_GETUSER 1  /* username has been requested */
27 #define STATE_AUTHEN_GETPASS 2  /* password has been requested */
28
29 #include <skey.h>
30
31 struct private_data {
32     struct skey skey;
33     char password[MAX_PASSWD_LEN + 1];
34     int state;
35 };
36
37 /* Use s/key to verify a supplied password using state set up earlier
38 when the username was supplied */
39
40 static int
41 skey_verify(passwd, data)
42 char *passwd;
43 struct authen_data *data;
44 {
45     struct private_data *p = data->method_data;
46     struct skey *skeyp = &p->skey;
47
48     data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
49
50     if (skeyverify(skeyp, passwd) == 0) {
51         /* S/Key authentication succeeded */
52         data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
53         if (skeyp->n < 5) {
54             data->server_msg = tac_strdup("Password will expire soon");
55             return (1);
56         }
57     }
58     return (0);
59 }
60
61 /*
62  * Skey tacacs login authentication function. Wants a username
63  * and a password, and tries to verify them via skey.
64  *
65  * Choose_authen will ensure that we already have a username before this
66  * gets called.
67  *
68  * We will query for a password and keep it in the method_data.
69  *
70  * Any strings returned via pointers in authen_data must come from the
71  * heap. They will get freed by the caller.
72  *
73  * Return 0 if data->status is valid, otherwise 1
74  */
75
76 int
77 skey_fn(data)
78 struct authen_data *data;
79 {
80     char *name, *passwd;
81     struct private_data *p;
82     char *prompt;
83     int pwlen;
84
85     p = (struct private_data *) data->method_data;
86
87     /* An abort has been received. Clean up and return */
88     if (data->flags & TAC_PLUS_CONTINUE_FLAG_ABORT) {
89         if (data->method_data)
90             free(data->method_data);
91         data->method_data = NULL;
92         return (1);
93     }
94     /* Initialise method_data if first time through */
95     if (!p) {
96         p = (struct private_data *) tac_malloc(sizeof(struct private_data));
97         bzero(p, sizeof(struct private_data));
98         data->method_data = p;
99         p->state = STATE_AUTHEN_START;
100     }
101
102     /* Unless we're enabling, we need a username */
103     if (data->service != TAC_PLUS_AUTHEN_SVC_ENABLE &&
104         !(char) data->NAS_id->username[0]) {
105         switch (p->state) {
106
107         case STATE_AUTHEN_GETUSER:
108             /* we have previously asked for a username but none came back.
109              * This is a gross error */
110             data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
111             report(LOG_ERR, "%s: No username supplied after GETUSER",
112                    session.peer);
113             return (0);
114
115         case STATE_AUTHEN_START:
116             /* No username. Try requesting one */
117             data->status = TAC_PLUS_AUTHEN_STATUS_GETUSER;
118             if (data->service == TAC_PLUS_AUTHEN_SVC_LOGIN) {
119                 prompt = "\nUser Access Verification\n\nUsername: ";
120             } else {
121                 prompt = "Username: ";
122             }
123             data->server_msg = tac_strdup(prompt);
124             p->state = STATE_AUTHEN_GETUSER;
125             return (0);
126
127         default:
128             /* something awful has happened. Give up and die */
129             report(LOG_ERR, "%s: skey_fn bad state %d", 
130                    session.peer, p->state);
131             return (1);
132         }
133     }
134
135     /* we now have a username if we needed one */
136     name = data->NAS_id->username;
137
138     /* Do we have a password? */
139     passwd = p->password;
140
141     if (!passwd[0]) {
142         char skeyprompt[80];
143
144         /* no password yet. Either we need to ask for one and expect to get
145          * called again, or we asked but nothing came back, which is fatal */
146
147         switch (p->state) {
148         case STATE_AUTHEN_GETPASS:
149             /* We already asked for a password. This should be the reply */
150             if (data->client_msg) {
151                 pwlen = MIN(strlen(data->client_msg), MAX_PASSWD_LEN);
152             } else {
153                 pwlen = 0;
154             }
155             strncpy(passwd, data->client_msg, pwlen);
156             passwd[pwlen] = '\0';
157             break;
158
159         default:
160             /* Request a password */
161             passwd = cfg_get_login_secret(name, TAC_PLUS_RECURSE);
162             if (!passwd && !STREQ(passwd, "skey")) {
163                 report(LOG_ERR, "Cannot find skey password declaration for %s",
164                        name);
165                 data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
166                 return(1);
167             }
168
169             if (skeychallenge(&p->skey, name, skeyprompt) == 0) {
170                 char buf[256];
171                 sprintf(buf, "%s\nPassword: ", skeyprompt);
172                 data->server_msg = tac_strdup(buf);
173                 data->status = TAC_PLUS_AUTHEN_STATUS_GETPASS;
174                 p->state = STATE_AUTHEN_GETPASS;
175                 return (0);
176             } 
177
178             data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
179             report(LOG_ERR, "Cannot generate skey prompt for %s", name);
180             return(1);
181         }
182     }
183
184     /* We have a username and password. Try validating */
185
186     /* Assume the worst */
187     data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
188
189     switch (data->service) {
190     case TAC_PLUS_AUTHEN_SVC_LOGIN:
191         skey_verify(passwd, data);
192         if (debug)
193             report(LOG_INFO, "login query for '%s' %s from %s %s",
194                    name && name[0] ? name : "unknown",
195                    data->NAS_id->NAS_port && data->NAS_id->NAS_port[0] ?
196                        data->NAS_id->NAS_port : "unknown",
197                    session.peer,
198                    (data->status == TAC_PLUS_AUTHEN_STATUS_PASS) ?
199                    "accepted" : "rejected");
200         break;
201
202     default:
203         data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
204         report(LOG_ERR, "%s: Bogus service value %d from packet", 
205                session.peer, data->service);
206         break;
207     }
208
209     if (data->method_data)
210         free(data->method_data);
211     data->method_data = NULL;
212
213     switch (data->status) {
214     case TAC_PLUS_AUTHEN_STATUS_ERROR:
215     case TAC_PLUS_AUTHEN_STATUS_FAIL:
216     case TAC_PLUS_AUTHEN_STATUS_PASS:
217         return (0);
218     default:
219         report(LOG_ERR, "%s: skey_fn couldn't set recognizable status %d",
220                session.peer, data->status);
221         data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
222         return (1);
223     }
224 }
225 #else /* SKEY */
226
227 /* The following code is not needed or used. It exists solely to
228    prevent compilers from "helpfully" complaining that this source
229    file is empty, which upsets novices building the software */
230
231 static int dummy = 0;
232
233 #endif /* SKEY */