Release bumped to "gts4".
[tac_plus.git] / pwlib.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 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif
26 #include <pwd.h>
27 #include <sys/types.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <time.h>
32
33 #ifdef SHADOW_PASSWORDS
34 #ifdef HAVE_SHADOW_H
35 #include <shadow.h>
36 #endif
37 #endif
38
39 #include "pwlib.h"
40 #include "expire.h"
41 #include "time_limit.h"
42 #include "report.h"
43 #include "utils.h"
44 #include "cfgfile.h"
45 #include "pw.h"
46 #include "choose_authen.h"                      /* for "struct authen_data" */
47 #include "packet.h"
48 #include "main.h"
49 #include "parse.h"
50
51 #ifdef USE_PAM
52 #include "tac_pam.h"
53 #endif
54 #ifdef DB
55 #include "db.h"                 /* For database verification */
56 #endif
57 #ifdef USE_LDAP
58 #include "ldap_author.h"                /* For LDAP verification */
59 #endif
60
61
62 static int passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data, const char *filename));
63
64
65 /* Generic password verification routines for des, file and cleartext
66    passwords */
67
68 /* Adjust data->status depending on whether a user has expired or not */
69
70 void set_expiration_status TAC_ARGS((const char *exp_date, struct authen_data *data));
71
72 void
73 set_expiration_status(exp_date, data)
74 const char *exp_date;
75 struct authen_data *data;
76 {
77     int expired;
78
79     /* if the status is anything except pass, there's no point proceeding */
80     if (data->status != TAC_PLUS_AUTHEN_STATUS_PASS) {
81         return;
82     }
83
84     /* Check the expiration date, if any. If NULL, this check will return
85      * PW_OK */
86     expired = check_expiration(exp_date);
87
88     switch (expired) {
89     case PW_OK:
90         if (debug & DEBUG_PASSWD_FLAG)
91             report(LOG_DEBUG, "Password has not expired %s",
92                    exp_date ? exp_date : "<no expiry date set>");
93
94         data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
95         return;
96
97     case PW_EXPIRING:
98         if (debug & DEBUG_PASSWD_FLAG)
99             report(LOG_DEBUG, "Password will expire soon %s",
100                    exp_date ? exp_date : "<no expiry date set>");
101         if (data->server_msg)
102             free(data->server_msg);
103         data->server_msg = tac_strdup("Password will expire soon");
104         data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
105         return;
106
107     case PW_EXPIRED:
108         if (debug & DEBUG_PASSWD_FLAG)
109             report(LOG_DEBUG, "Password has expired %s",
110                    exp_date ? exp_date : "<no expiry date set>");
111         if (data->server_msg)
112             free(data->server_msg);
113         data->server_msg = tac_strdup("Password has expired");
114         data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
115         return;
116
117     default:
118         report(LOG_ERR, "%s: Bogus return value %d from check_expiration",
119                session.peer, expired);
120         data->status = TAC_PLUS_AUTHEN_STATUS_ERROR;
121         return;
122     }
123 }
124
125 /* Verify that this user/password is valid.  Works only for cleartext,
126    file and des passwords.
127
128    Return 1 if password is valid */
129
130 int verify TAC_ARGS((const char *name, const char *passwd, struct authen_data *data, int recurse));
131
132 int
133 verify(name, passwd, data, recurse)
134 const char *name;
135 const char *passwd;
136 struct authen_data *data;
137 int recurse;
138 {
139     const char *exp_date, *cfg_passwd, *p, *timestamp;
140
141     timestamp = cfg_get_timestamp(name, recurse);
142     if ( timestamp != NULL ) {
143         if( time_limit_process(timestamp) == 0  ) {
144                 if ( debug & DEBUG_AUTHEN_FLAG )
145                         report(LOG_DEBUG,"Timestamp check failed");
146                 data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
147                 return (0);
148         }
149     }
150
151     if (data->type == TAC_PLUS_AUTHEN_TYPE_PAP) {
152         cfg_passwd = cfg_get_pap_secret(name, recurse);
153     } else {
154         cfg_passwd = cfg_get_login_secret(name, recurse);
155     }
156
157     /* If there is no login or pap password for this user, see if there is
158        a global password for her that we can use */
159
160     if (!cfg_passwd) {
161         cfg_passwd = cfg_get_global_secret(name, recurse);
162     }
163
164     /* If we still have no password for this user (or no user for that
165        matter) but the default authentication = file <file> statement
166        has been issued, attempt to use this password file */
167
168     if (!cfg_passwd) {
169         const char *file = cfg_get_authen_default();
170         switch (cfg_get_authen_default_method()) {
171
172         case (S_file):
173             if (file)
174                 return (passwd_file_verify(name, passwd, data, file));
175             break;
176
177 #ifdef DB
178         case (S_db):
179         /* ugly check for database connect string */
180         if( strstr(file, "://") ) {
181              if (debug & DEBUG_PASSWD_FLAG)
182                  report(LOG_DEBUG,"%s %s: DB access to %s for user %s",session.peer, session.port, file, name);
183              if (!db_verify(name, passwd, file)) {
184                  data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
185                  return (0);
186              } else {
187                  data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
188              }
189              exp_date = NULL;
190              set_expiration_status(exp_date, data);
191              return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
192         }
193         break;
194 #endif
195
196 #ifdef USE_LDAP
197         case (S_ldap):
198         if (ldap_verify(name, passwd, file)==1) {
199             data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
200             return (0);
201         } else {
202             data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
203         }
204         exp_date = NULL;
205         set_expiration_status(exp_date, data);
206         return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
207         break;
208 #endif /* USE_LDAP */
209
210 #ifdef USE_PAM
211         case (S_pam):
212         if (debug & DEBUG_PASSWD_FLAG)
213             report(LOG_DEBUG, "PAM verify daemon [PAM] == NAS %s", passwd);
214         if (tac_pam_auth(name, passwd, data, file)) {
215             if (debug & DEBUG_PASSWD_FLAG)
216                 report(LOG_DEBUG, "PAM default authentication fail");
217             data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
218             return(0);
219         } else {
220             data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
221
222             if (debug & DEBUG_PASSWD_FLAG)
223                 report(LOG_DEBUG, " PAM default authentication pass");
224         }
225
226         exp_date = cfg_get_expires(name, recurse);
227         set_expiration_status(exp_date, data);
228         return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
229         break;
230 #endif
231         default:
232         /* otherwise, we fail */
233         data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
234         return (0);
235         }
236     }
237
238     /* We have a configured password. Deal with it depending on its
239        type */
240
241
242     p = tac_find_substring("cleartext ", cfg_passwd);
243     if (p) {
244         if (debug & DEBUG_PASSWD_FLAG)
245             report(LOG_DEBUG, "verify daemon %s == NAS %s", p, passwd);
246
247         if (strcmp(passwd, p)) {
248             if (debug & DEBUG_PASSWD_FLAG)
249                 report(LOG_DEBUG, "Password is incorrect");
250             data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
251             return(0);
252         } else {
253             data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
254
255             if (debug & DEBUG_PASSWD_FLAG)
256                 report(LOG_DEBUG, "Password is correct");
257         }
258
259         exp_date = cfg_get_expires(name, recurse);
260         set_expiration_status(exp_date, data);
261         return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
262     }
263
264 #ifdef USE_PAM
265     p = tac_find_substring("pam ", cfg_passwd);
266     if (p) {
267         if (debug & DEBUG_PASSWD_FLAG)
268             report(LOG_DEBUG, "PAM verify daemon %s == NAS %s", p,passwd);
269
270         if (tac_pam_auth(name, passwd, data,p)) {
271             if (debug & DEBUG_PASSWD_FLAG)
272                 report(LOG_DEBUG, "PAM Password is incorrect");
273             data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
274             return(0);
275         } else {
276             data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
277
278             if (debug & DEBUG_PASSWD_FLAG)
279                 report(LOG_DEBUG, "PAM Password is correct");
280         }
281
282         exp_date = cfg_get_expires(name, recurse);
283         set_expiration_status(exp_date, data);
284         return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
285     }
286
287 #endif /* USE_PAM */
288
289     p = tac_find_substring("des ", cfg_passwd);
290     if (p) {
291         /* try to verify this des password */
292         if (!des_verify(passwd, p)) {
293             data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
294             return (0);
295         } else {
296             data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
297         }
298
299         exp_date = cfg_get_expires(name, recurse);
300         set_expiration_status(exp_date, data);
301         return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
302     }
303
304 #ifdef DB
305     p = tac_find_substring("db ", cfg_passwd);
306     if (p) {
307         /* try to verify this password from database */
308         if (debug & DEBUG_PASSWD_FLAG)
309             report(LOG_DEBUG, "DB verify daemon %s == NAS %s", p, passwd);
310
311         if (!db_verify(name, passwd, p)) {
312             data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
313
314         if (debug & DEBUG_PASSWD_FLAG)
315                 report(LOG_DEBUG, "DB Password is incorrect");
316
317          return (0);
318         } else {
319
320         if (debug & DEBUG_PASSWD_FLAG)
321                 report(LOG_DEBUG, "DB Password is correct");
322             data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
323         }
324         exp_date = cfg_get_expires(name, recurse);
325         set_expiration_status(exp_date, data);
326         return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
327     }
328 #endif /* DB */
329
330     p = tac_find_substring("file ", cfg_passwd);
331     if (p) {
332         return (passwd_file_verify(name, passwd, data, p));
333     }
334
335     /* Oops. No idea what kind of password this is. This should never
336        happen as the parser should never create such passwords. */
337
338     report(LOG_ERR, "%s: Error cannot identify password type %s for %s",
339            session.peer,
340            cfg_passwd && cfg_passwd[0] ? cfg_passwd : "<NULL>",
341            name ? name : "<unknown>");
342
343     data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
344     return (0);
345 }
346
347 static int etc_passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data));
348
349 /* verify that this user/password is valid per /etc/passwd.
350  * Return 0 if invalid.
351  */
352 static int
353 etc_passwd_file_verify(user, supplied_passwd, data)
354 const char *user;
355 const char *supplied_passwd;
356 struct authen_data *data;
357 {
358     struct passwd *pw;
359     char *exp_date;
360     char *cfg_passwd;
361 #ifdef SHADOW_PASSWORDS
362     char buf[12];
363 #endif /* SHADOW_PASSWORDS */
364
365     data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
366
367     setpwent();
368     pw = getpwnam(user);
369     endpwent();
370
371     if (pw == NULL) {
372         /* no entry exists */
373         return (0);
374     }
375
376     if (*pw->pw_passwd == '\0' ||
377         supplied_passwd == NULL ||
378         *supplied_passwd == '\0') {
379         data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
380         return (0);
381     }
382     cfg_passwd = pw->pw_passwd;
383     exp_date = pw->pw_shell;
384
385 #ifdef SHADOW_PASSWORDS
386     if (STREQ(pw->pw_passwd, "x")) {
387         struct spwd *spwd = getspnam(user);
388
389         if (!spwd) {
390             if (debug & DEBUG_PASSWD_FLAG) {
391                 report(LOG_DEBUG, "No entry for %s in shadow file", user);
392             }
393             data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
394             return (0);
395         }
396         if (debug & DEBUG_PASSWD_FLAG) {
397             report(LOG_DEBUG, "Found entry for %s in shadow file", user);
398         }
399         cfg_passwd = spwd->sp_pwdp;
400
401         /*
402          * Sigh. The Solaris shadow password file contains its own
403          * expiry date as the number of days after the epoch
404          * (January 1, 1970) when the password expires.
405          * Convert this to ascii so that the traditional tacacs
406          * password expiration routines work correctly.
407          */
408
409         if (spwd->sp_expire > 0) {
410             long secs = spwd->sp_expire * 24 * 60 * 60;
411             char *p = ctime(&secs);
412             bcopy(p+4, buf, 7);
413             bcopy(p+20, buf+7, 4);
414             buf[11] = '\0';
415             exp_date = buf;
416         }
417     }
418 #endif /* SHADOW_PASSWORDS */
419
420     /* try to verify the password */
421     if (!des_verify(supplied_passwd, cfg_passwd)) {
422         data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
423         return (0);
424     } else {
425         data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
426     }
427
428     /* password ok. Check expiry field */
429     set_expiration_status(exp_date, data);
430
431     return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
432 }
433
434 /* verify that this user/password is valid per a passwd(5) style
435    database. Return 0 if invalid. */
436
437 static int passwd_file_verify TAC_ARGS((const char *user, const char *supplied_passwd, struct authen_data *data, const char *filename));
438
439 static int
440 passwd_file_verify(user, supplied_passwd, data, filename)
441 const char *user;
442 const char *supplied_passwd;
443 struct authen_data *data;
444 const char *filename;
445 {
446     struct passwd *pw;
447     char *exp_date;
448     char *cfg_passwd;
449
450     data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
451
452     if (filename && (STREQ(filename, "/etc/passwd")|| STREQ(filename,"/etc/shadow") )) {
453         return(etc_passwd_file_verify(user, supplied_passwd, data));
454     }
455
456
457
458     /* an alternate filename */
459     if (!(access(filename, R_OK) == 0)) {
460         report(LOG_ERR, "%s %s: Cannot access %s for user %s -- %s",
461                session.peer, session.port, filename, user, sys_errlist[errno]);
462         return (0);
463     }
464
465     pw = tac_passwd_lookup(user, filename);
466
467     if (pw == NULL)
468         /* no entry exists */
469         return (0);
470
471     if (*pw->pw_passwd == '\0' ||
472         supplied_passwd == NULL ||
473         *supplied_passwd == '\0') {
474         data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
475         return (0);
476     }
477     cfg_passwd = pw->pw_passwd;
478     exp_date = pw->pw_shell;
479
480     /* try to verify the password */
481     if (!des_verify(supplied_passwd, cfg_passwd)) {
482         data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
483         return (0);
484     } else {
485         data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
486     }
487
488     /* password ok. Check expiry field */
489     set_expiration_status(exp_date, data);
490     return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
491 }
492
493 /*
494  * verify a provided password against a des encrypted one
495  * return 1 if verified, 0 otherwise.
496  */
497
498 int des_verify TAC_ARGS((const char *users_passwd, const char *encrypted_passwd));
499
500 int
501 des_verify(users_passwd, encrypted_passwd)
502 const char *users_passwd;
503 const char *encrypted_passwd;
504 {
505     char *ep;
506
507     if (debug & DEBUG_PASSWD_FLAG)
508         report(LOG_DEBUG, "verify %s %s", users_passwd, encrypted_passwd);
509
510     if (users_passwd == NULL ||
511         *users_passwd == '\0' ||
512         encrypted_passwd == NULL ||
513         *encrypted_passwd == '\0') {
514         if (debug & DEBUG_PASSWD_FLAG)
515             report(LOG_DEBUG, "verify returns 0");
516         return (0);
517     }
518
519     ep = (char *) crypt(users_passwd, encrypted_passwd);
520
521     if (debug & DEBUG_PASSWD_FLAG)
522         report(LOG_DEBUG, "%s encrypts to %s", users_passwd, ep);
523
524     if (strcmp(ep, encrypted_passwd) == 0) {
525         if (debug & DEBUG_PASSWD_FLAG)
526             report(LOG_DEBUG, "Password is correct");
527         return (1);
528     }
529
530     if (debug & DEBUG_PASSWD_FLAG)
531         report(LOG_DEBUG, "Password is incorrect");
532
533     return (0);
534 }