r3220: merging current 3.0 code to release branch
[Samba.git] / source / auth / pass_check.c
blob0425e01cdcb6c8880fa62fba4c7f25ddd8ab42b1
1 /*
2 Unix SMB/CIFS implementation.
3 Password checking
4 Copyright (C) Andrew Tridgell 1992-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 /* this module is for checking a username/password against a system
22 password database. The SMB encrypted password support is elsewhere */
24 #include "includes.h"
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_AUTH
29 /* these are kept here to keep the string_combinations function simple */
30 static fstring this_user;
31 #if !defined(WITH_PAM)
32 static fstring this_salt;
33 static fstring this_crypted;
34 #endif
36 #ifdef WITH_AFS
38 #include <afs/stds.h>
39 #include <afs/kautils.h>
41 /*******************************************************************
42 check on AFS authentication
43 ********************************************************************/
44 static BOOL afs_auth(char *user, char *password)
46 long password_expires = 0;
47 char *reason;
49 /* For versions of AFS prior to 3.3, this routine has few arguments, */
50 /* but since I can't find the old documentation... :-) */
51 setpag();
52 if (ka_UserAuthenticateGeneral
53 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
54 (char *)0, /* cell */
55 password, 0, /* lifetime, default */
56 &password_expires, /*days 'til it expires */
57 0, /* spare 2 */
58 &reason) == 0)
60 return (True);
62 DEBUG(1,
63 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
64 return (False);
66 #endif
69 #ifdef WITH_DFS
71 #include <dce/dce_error.h>
72 #include <dce/sec_login.h>
74 /*****************************************************************
75 This new version of the DFS_AUTH code was donated by Karsten Muuss
76 <muuss@or.uni-bonn.de>. It fixes the following problems with the
77 old code :
79 - Server credentials may expire
80 - Client credential cache files have wrong owner
81 - purge_context() function is called with invalid argument
83 This new code was modified to ensure that on exit the uid/gid is
84 still root, and the original directory is restored. JRA.
85 ******************************************************************/
87 sec_login_handle_t my_dce_sec_context;
88 int dcelogin_atmost_once = 0;
90 /*******************************************************************
91 check on a DCE/DFS authentication
92 ********************************************************************/
93 static BOOL dfs_auth(char *user, char *password)
95 error_status_t err;
96 int err2;
97 int prterr;
98 signed32 expire_time, current_time;
99 boolean32 password_reset;
100 struct passwd *pw;
101 sec_passwd_rec_t passwd_rec;
102 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
103 unsigned char dce_errstr[dce_c_error_string_len];
104 gid_t egid;
106 if (dcelogin_atmost_once)
107 return (False);
109 #ifdef HAVE_CRYPT
111 * We only go for a DCE login context if the given password
112 * matches that stored in the local password file..
113 * Assumes local passwd file is kept in sync w/ DCE RGY!
116 if (strcmp((char *)crypt(password, this_salt), this_crypted))
118 return (False);
120 #endif
122 sec_login_get_current_context(&my_dce_sec_context, &err);
123 if (err != error_status_ok)
125 dce_error_inq_text(err, dce_errstr, &err2);
126 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
128 return (False);
131 sec_login_certify_identity(my_dce_sec_context, &err);
132 if (err != error_status_ok)
134 dce_error_inq_text(err, dce_errstr, &err2);
135 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
137 return (False);
140 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
141 if (err != error_status_ok)
143 dce_error_inq_text(err, dce_errstr, &err2);
144 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
146 return (False);
149 time(&current_time);
151 if (expire_time < (current_time + 60))
153 struct passwd *pw;
154 sec_passwd_rec_t *key;
156 sec_login_get_pwent(my_dce_sec_context,
157 (sec_login_passwd_t *) & pw, &err);
158 if (err != error_status_ok)
160 dce_error_inq_text(err, dce_errstr, &err2);
161 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
163 return (False);
166 sec_login_refresh_identity(my_dce_sec_context, &err);
167 if (err != error_status_ok)
169 dce_error_inq_text(err, dce_errstr, &err2);
170 DEBUG(0, ("DCE can't refresh identity. %s\n",
171 dce_errstr));
173 return (False);
176 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
177 (unsigned char *)pw->pw_name,
178 sec_c_key_version_none,
179 (void **)&key, &err);
180 if (err != error_status_ok)
182 dce_error_inq_text(err, dce_errstr, &err2);
183 DEBUG(0, ("DCE can't get key for %s. %s\n",
184 pw->pw_name, dce_errstr));
186 return (False);
189 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
190 &password_reset, &auth_src,
191 &err);
192 if (err != error_status_ok)
194 dce_error_inq_text(err, dce_errstr, &err2);
195 DEBUG(0,
196 ("DCE can't validate and certify identity for %s. %s\n",
197 pw->pw_name, dce_errstr));
200 sec_key_mgmt_free_key(key, &err);
201 if (err != error_status_ok)
203 dce_error_inq_text(err, dce_errstr, &err2);
204 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
208 if (sec_login_setup_identity((unsigned char *)user,
209 sec_login_no_flags,
210 &my_dce_sec_context, &err) == 0)
212 dce_error_inq_text(err, dce_errstr, &err2);
213 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
214 user, dce_errstr));
215 return (False);
218 sec_login_get_pwent(my_dce_sec_context,
219 (sec_login_passwd_t *) & pw, &err);
220 if (err != error_status_ok)
222 dce_error_inq_text(err, dce_errstr, &err2);
223 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
225 return (False);
228 sec_login_purge_context(&my_dce_sec_context, &err);
229 if (err != error_status_ok)
231 dce_error_inq_text(err, dce_errstr, &err2);
232 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
234 return (False);
238 * NB. I'd like to change these to call something like change_to_user()
239 * instead but currently we don't have a connection
240 * context to become the correct user. This is already
241 * fairly platform specific code however, so I think
242 * this should be ok. I have added code to go
243 * back to being root on error though. JRA.
246 egid = getegid();
248 set_effective_gid(pw->pw_gid);
249 set_effective_uid(pw->pw_uid);
251 if (sec_login_setup_identity((unsigned char *)user,
252 sec_login_no_flags,
253 &my_dce_sec_context, &err) == 0)
255 dce_error_inq_text(err, dce_errstr, &err2);
256 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
257 user, dce_errstr));
258 goto err;
261 sec_login_get_pwent(my_dce_sec_context,
262 (sec_login_passwd_t *) & pw, &err);
263 if (err != error_status_ok)
265 dce_error_inq_text(err, dce_errstr, &err2);
266 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
267 goto err;
270 passwd_rec.version_number = sec_passwd_c_version_none;
271 passwd_rec.pepper = NULL;
272 passwd_rec.key.key_type = sec_passwd_plain;
273 passwd_rec.key.tagged_union.plain = (idl_char *) password;
275 sec_login_validate_identity(my_dce_sec_context,
276 &passwd_rec, &password_reset,
277 &auth_src, &err);
278 if (err != error_status_ok)
280 dce_error_inq_text(err, dce_errstr, &err2);
281 DEBUG(0,
282 ("DCE Identity Validation failed for principal %s: %s\n",
283 user, dce_errstr));
284 goto err;
287 sec_login_certify_identity(my_dce_sec_context, &err);
288 if (err != error_status_ok)
290 dce_error_inq_text(err, dce_errstr, &err2);
291 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
292 goto err;
295 if (auth_src != sec_login_auth_src_network)
297 DEBUG(0, ("DCE context has no network credentials.\n"));
300 sec_login_set_context(my_dce_sec_context, &err);
301 if (err != error_status_ok)
303 dce_error_inq_text(err, dce_errstr, &err2);
304 DEBUG(0,
305 ("DCE login failed for principal %s, cant set context: %s\n",
306 user, dce_errstr));
308 sec_login_purge_context(&my_dce_sec_context, &err);
309 goto err;
312 sec_login_get_pwent(my_dce_sec_context,
313 (sec_login_passwd_t *) & pw, &err);
314 if (err != error_status_ok)
316 dce_error_inq_text(err, dce_errstr, &err2);
317 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
318 goto err;
321 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
322 user, sys_getpid()));
324 DEBUG(3, ("DCE principal: %s\n"
325 " uid: %d\n"
326 " gid: %d\n",
327 pw->pw_name, pw->pw_uid, pw->pw_gid));
328 DEBUG(3, (" info: %s\n"
329 " dir: %s\n"
330 " shell: %s\n",
331 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
333 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
334 if (err != error_status_ok)
336 dce_error_inq_text(err, dce_errstr, &err2);
337 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
338 goto err;
341 set_effective_uid(0);
342 set_effective_gid(0);
344 DEBUG(0,
345 ("DCE context expires: %s", asctime(localtime(&expire_time))));
347 dcelogin_atmost_once = 1;
348 return (True);
350 err:
352 /* Go back to root, JRA. */
353 set_effective_uid(0);
354 set_effective_gid(egid);
355 return (False);
358 void dfs_unlogin(void)
360 error_status_t err;
361 int err2;
362 unsigned char dce_errstr[dce_c_error_string_len];
364 sec_login_purge_context(&my_dce_sec_context, &err);
365 if (err != error_status_ok)
367 dce_error_inq_text(err, dce_errstr, &err2);
368 DEBUG(0,
369 ("DCE purge login context failed for server instance %d: %s\n",
370 sys_getpid(), dce_errstr));
373 #endif
375 #ifdef LINUX_BIGCRYPT
376 /****************************************************************************
377 an enhanced crypt for Linux to handle password longer than 8 characters
378 ****************************************************************************/
379 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
381 #define LINUX_PASSWORD_SEG_CHARS 8
382 char salt[3];
383 int i;
385 StrnCpy(salt, salt1, 2);
386 crypted += 2;
388 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
389 char *p = crypt(password, salt) + 2;
390 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
391 return (0);
392 password += LINUX_PASSWORD_SEG_CHARS;
393 crypted += strlen(p);
396 return (1);
398 #endif
400 #ifdef OSF1_ENH_SEC
401 /****************************************************************************
402 an enhanced crypt for OSF1
403 ****************************************************************************/
404 static char *osf1_bigcrypt(char *password, char *salt1)
406 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
407 char *p1;
408 char *p2 = password;
409 char salt[3];
410 int i;
411 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
412 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
413 parts++;
415 StrnCpy(salt, salt1, 2);
416 StrnCpy(result, salt1, 2);
417 result[2] = '\0';
419 for (i = 0; i < parts; i++) {
420 p1 = crypt(p2, salt);
421 strncat(result, p1 + 2,
422 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
423 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
424 p2 += AUTH_CLEARTEXT_SEG_CHARS;
427 return (result);
429 #endif
432 /****************************************************************************
433 apply a function to upper/lower case combinations
434 of a string and return true if one of them returns true.
435 try all combinations with N uppercase letters.
436 offset is the first char to try and change (start with 0)
437 it assumes the string starts lowercased
438 ****************************************************************************/
439 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
440 int N)
442 int len = strlen(s);
443 int i;
444 NTSTATUS nt_status;
446 #ifdef PASSWORD_LENGTH
447 len = MIN(len, PASSWORD_LENGTH);
448 #endif
450 if (N <= 0 || offset >= len)
451 return (fn(s));
453 for (i = offset; i < (len - (N - 1)); i++) {
454 char c = s[i];
455 if (!islower(c))
456 continue;
457 s[i] = toupper(c);
458 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
459 return (nt_status);
461 s[i] = c;
463 return (NT_STATUS_WRONG_PASSWORD);
466 /****************************************************************************
467 apply a function to upper/lower case combinations
468 of a string and return true if one of them returns true.
469 try all combinations with up to N uppercase letters.
470 offset is the first char to try and change (start with 0)
471 it assumes the string starts lowercased
472 ****************************************************************************/
473 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
475 int n;
476 NTSTATUS nt_status;
477 for (n = 1; n <= N; n++)
478 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
479 return nt_status;
480 return NT_STATUS_WRONG_PASSWORD;
484 /****************************************************************************
485 core of password checking routine
486 ****************************************************************************/
487 static NTSTATUS password_check(const char *password)
489 #ifdef WITH_PAM
490 return smb_pam_passcheck(this_user, password);
491 #else
493 BOOL ret;
495 #ifdef WITH_AFS
496 if (afs_auth(this_user, password))
497 return NT_STATUS_OK;
498 #endif /* WITH_AFS */
500 #ifdef WITH_DFS
501 if (dfs_auth(this_user, password))
502 return NT_STATUS_OK;
503 #endif /* WITH_DFS */
505 #ifdef OSF1_ENH_SEC
507 ret = (strcmp(osf1_bigcrypt(password, this_salt),
508 this_crypted) == 0);
509 if (!ret) {
510 DEBUG(2,
511 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
512 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
514 if (ret) {
515 return NT_STATUS_OK;
516 } else {
517 return NT_STATUS_WRONG_PASSWORD;
520 #endif /* OSF1_ENH_SEC */
522 #ifdef ULTRIX_AUTH
523 ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
524 if (ret) {
525 return NT_STATUS_OK;
526 } else {
527 return NT_STATUS_WRONG_PASSWORD;
530 #endif /* ULTRIX_AUTH */
532 #ifdef LINUX_BIGCRYPT
533 ret = (linux_bigcrypt(password, this_salt, this_crypted));
534 if (ret) {
535 return NT_STATUS_OK;
536 } else {
537 return NT_STATUS_WRONG_PASSWORD;
539 #endif /* LINUX_BIGCRYPT */
541 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
544 * Some systems have bigcrypt in the C library but might not
545 * actually use it for the password hashes (HPUX 10.20) is
546 * a noteable example. So we try bigcrypt first, followed
547 * by crypt.
550 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
551 return NT_STATUS_OK;
552 else
553 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
554 if (ret) {
555 return NT_STATUS_OK;
556 } else {
557 return NT_STATUS_WRONG_PASSWORD;
559 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
561 #ifdef HAVE_BIGCRYPT
562 ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
563 if (ret) {
564 return NT_STATUS_OK;
565 } else {
566 return NT_STATUS_WRONG_PASSWORD;
568 #endif /* HAVE_BIGCRYPT */
570 #ifndef HAVE_CRYPT
571 DEBUG(1, ("Warning - no crypt available\n"));
572 return NT_STATUS_LOGON_FAILURE;
573 #else /* HAVE_CRYPT */
574 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
575 if (ret) {
576 return NT_STATUS_OK;
577 } else {
578 return NT_STATUS_WRONG_PASSWORD;
580 #endif /* HAVE_CRYPT */
581 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
582 #endif /* WITH_PAM */
587 /****************************************************************************
588 CHECK if a username/password is OK
589 the function pointer fn() points to a function to call when a successful
590 match is found and is used to update the encrypted password file
591 return NT_STATUS_OK on correct match, appropriate error otherwise
592 ****************************************************************************/
594 NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password,
595 int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
597 pstring pass2;
598 int level = lp_passwordlevel();
600 NTSTATUS nt_status;
602 #ifdef DEBUG_PASSWORD
603 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
604 #endif
606 if (!password)
607 return NT_STATUS_LOGON_FAILURE;
609 if (((!*password) || (!pwlen)) && !lp_null_passwords())
610 return NT_STATUS_LOGON_FAILURE;
612 #if defined(WITH_PAM)
615 * If we're using PAM we want to short-circuit all the
616 * checks below and dive straight into the PAM code.
619 fstrcpy(this_user, user);
621 DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
623 #else /* Not using PAM */
625 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
627 if (!pass) {
628 DEBUG(3, ("Couldn't find user %s\n", user));
629 return NT_STATUS_NO_SUCH_USER;
633 /* Copy into global for the convenience of looping code */
634 /* Also the place to keep the 'password' no matter what
635 crazy struct it started in... */
636 fstrcpy(this_crypted, pass->pw_passwd);
637 fstrcpy(this_salt, pass->pw_passwd);
639 #ifdef HAVE_GETSPNAM
641 struct spwd *spass;
643 /* many shadow systems require you to be root to get
644 the password, in most cases this should already be
645 the case when this function is called, except
646 perhaps for IPC password changing requests */
648 spass = getspnam(pass->pw_name);
649 if (spass && spass->sp_pwdp) {
650 fstrcpy(this_crypted, spass->sp_pwdp);
651 fstrcpy(this_salt, spass->sp_pwdp);
654 #elif defined(IA_UINFO)
656 /* Need to get password with SVR4.2's ia_ functions
657 instead of get{sp,pw}ent functions. Required by
658 UnixWare 2.x, tested on version
659 2.1. (tangent@cyberport.com) */
660 uinfo_t uinfo;
661 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
662 ia_get_logpwd(uinfo, &(pass->pw_passwd));
664 #endif
666 #ifdef HAVE_GETPRPWNAM
668 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
669 if (pr_pw && pr_pw->ufld.fd_encrypt)
670 fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
672 #endif
674 #ifdef HAVE_GETPWANAM
676 struct passwd_adjunct *pwret;
677 pwret = getpwanam(s);
678 if (pwret && pwret->pwa_passwd)
679 fstrcpy(this_crypted, pwret->pwa_passwd);
681 #endif
683 #ifdef OSF1_ENH_SEC
685 struct pr_passwd *mypasswd;
686 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
687 user));
688 mypasswd = getprpwnam(user);
689 if (mypasswd) {
690 fstrcpy(this_user, mypasswd->ufld.fd_name);
691 fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
692 } else {
693 DEBUG(5,
694 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
695 user));
698 #endif
700 #ifdef ULTRIX_AUTH
702 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
703 if (ap) {
704 fstrcpy(this_crypted, ap->a_password);
705 endauthent();
708 #endif
710 #if defined(HAVE_TRUNCATED_SALT)
711 /* crypt on some platforms (HPUX in particular)
712 won't work with more than 2 salt characters. */
713 this_salt[2] = 0;
714 #endif
716 if (!*this_crypted) {
717 if (!lp_null_passwords()) {
718 DEBUG(2, ("Disallowing %s with null password\n",
719 this_user));
720 return NT_STATUS_LOGON_FAILURE;
722 if (!*password) {
723 DEBUG(3,
724 ("Allowing access to %s with null password\n",
725 this_user));
726 return NT_STATUS_OK;
730 #endif /* defined(WITH_PAM) */
732 /* try it as it came to us */
733 nt_status = password_check(password);
734 if NT_STATUS_IS_OK(nt_status) {
735 if (fn) {
736 fn(user, password);
738 return (nt_status);
739 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
740 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
741 return (nt_status);
744 if (!run_cracker) {
745 return (nt_status);
748 /* if the password was given to us with mixed case then we don't
749 * need to proceed as we know it hasn't been case modified by the
750 * client */
751 if (strhasupper(password) && strhaslower(password)) {
752 return nt_status;
755 /* make a copy of it */
756 pstrcpy(pass2, password);
758 /* try all lowercase if it's currently all uppercase */
759 if (strhasupper(pass2)) {
760 strlower_m(pass2);
761 if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
762 if (fn)
763 fn(user, pass2);
764 return (nt_status);
768 /* give up? */
769 if (level < 1) {
770 return NT_STATUS_WRONG_PASSWORD;
773 /* last chance - all combinations of up to level chars upper! */
774 strlower_m(pass2);
776 if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
777 if (fn)
778 fn(user, pass2);
779 return nt_status;
782 return NT_STATUS_WRONG_PASSWORD;