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