[GLUE] Rsync SAMBA_3_0 SVN r25598 in order to create the v3-0-test branch.
[Samba.git] / source / auth / pass_check.c
blobd0a900b80f33d408be027ddea5fc7cbdb82a57ec
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 struct tm *t;
96 error_status_t err;
97 int err2;
98 int prterr;
99 signed32 expire_time, current_time;
100 boolean32 password_reset;
101 struct passwd *pw;
102 sec_passwd_rec_t passwd_rec;
103 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
104 unsigned char dce_errstr[dce_c_error_string_len];
105 gid_t egid;
107 if (dcelogin_atmost_once)
108 return (False);
110 #ifdef HAVE_CRYPT
112 * We only go for a DCE login context if the given password
113 * matches that stored in the local password file..
114 * Assumes local passwd file is kept in sync w/ DCE RGY!
117 if (strcmp((char *)crypt(password, this_salt), this_crypted))
119 return (False);
121 #endif
123 sec_login_get_current_context(&my_dce_sec_context, &err);
124 if (err != error_status_ok)
126 dce_error_inq_text(err, dce_errstr, &err2);
127 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
129 return (False);
132 sec_login_certify_identity(my_dce_sec_context, &err);
133 if (err != error_status_ok)
135 dce_error_inq_text(err, dce_errstr, &err2);
136 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
138 return (False);
141 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
142 if (err != error_status_ok)
144 dce_error_inq_text(err, dce_errstr, &err2);
145 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
147 return (False);
150 time(&current_time);
152 if (expire_time < (current_time + 60))
154 struct passwd *pw;
155 sec_passwd_rec_t *key;
157 sec_login_get_pwent(my_dce_sec_context,
158 (sec_login_passwd_t *) & pw, &err);
159 if (err != error_status_ok)
161 dce_error_inq_text(err, dce_errstr, &err2);
162 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
164 return (False);
167 sec_login_refresh_identity(my_dce_sec_context, &err);
168 if (err != error_status_ok)
170 dce_error_inq_text(err, dce_errstr, &err2);
171 DEBUG(0, ("DCE can't refresh identity. %s\n",
172 dce_errstr));
174 return (False);
177 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
178 (unsigned char *)pw->pw_name,
179 sec_c_key_version_none,
180 (void **)&key, &err);
181 if (err != error_status_ok)
183 dce_error_inq_text(err, dce_errstr, &err2);
184 DEBUG(0, ("DCE can't get key for %s. %s\n",
185 pw->pw_name, dce_errstr));
187 return (False);
190 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
191 &password_reset, &auth_src,
192 &err);
193 if (err != error_status_ok)
195 dce_error_inq_text(err, dce_errstr, &err2);
196 DEBUG(0,
197 ("DCE can't validate and certify identity for %s. %s\n",
198 pw->pw_name, dce_errstr));
201 sec_key_mgmt_free_key(key, &err);
202 if (err != error_status_ok)
204 dce_error_inq_text(err, dce_errstr, &err2);
205 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
209 if (sec_login_setup_identity((unsigned char *)user,
210 sec_login_no_flags,
211 &my_dce_sec_context, &err) == 0)
213 dce_error_inq_text(err, dce_errstr, &err2);
214 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
215 user, dce_errstr));
216 return (False);
219 sec_login_get_pwent(my_dce_sec_context,
220 (sec_login_passwd_t *) & pw, &err);
221 if (err != error_status_ok)
223 dce_error_inq_text(err, dce_errstr, &err2);
224 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
226 return (False);
229 sec_login_purge_context(&my_dce_sec_context, &err);
230 if (err != error_status_ok)
232 dce_error_inq_text(err, dce_errstr, &err2);
233 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
235 return (False);
239 * NB. I'd like to change these to call something like change_to_user()
240 * instead but currently we don't have a connection
241 * context to become the correct user. This is already
242 * fairly platform specific code however, so I think
243 * this should be ok. I have added code to go
244 * back to being root on error though. JRA.
247 egid = getegid();
249 set_effective_gid(pw->pw_gid);
250 set_effective_uid(pw->pw_uid);
252 if (sec_login_setup_identity((unsigned char *)user,
253 sec_login_no_flags,
254 &my_dce_sec_context, &err) == 0)
256 dce_error_inq_text(err, dce_errstr, &err2);
257 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
258 user, dce_errstr));
259 goto err;
262 sec_login_get_pwent(my_dce_sec_context,
263 (sec_login_passwd_t *) & pw, &err);
264 if (err != error_status_ok)
266 dce_error_inq_text(err, dce_errstr, &err2);
267 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
268 goto err;
271 passwd_rec.version_number = sec_passwd_c_version_none;
272 passwd_rec.pepper = NULL;
273 passwd_rec.key.key_type = sec_passwd_plain;
274 passwd_rec.key.tagged_union.plain = (idl_char *) password;
276 sec_login_validate_identity(my_dce_sec_context,
277 &passwd_rec, &password_reset,
278 &auth_src, &err);
279 if (err != error_status_ok)
281 dce_error_inq_text(err, dce_errstr, &err2);
282 DEBUG(0,
283 ("DCE Identity Validation failed for principal %s: %s\n",
284 user, dce_errstr));
285 goto err;
288 sec_login_certify_identity(my_dce_sec_context, &err);
289 if (err != error_status_ok)
291 dce_error_inq_text(err, dce_errstr, &err2);
292 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
293 goto err;
296 if (auth_src != sec_login_auth_src_network)
298 DEBUG(0, ("DCE context has no network credentials.\n"));
301 sec_login_set_context(my_dce_sec_context, &err);
302 if (err != error_status_ok)
304 dce_error_inq_text(err, dce_errstr, &err2);
305 DEBUG(0,
306 ("DCE login failed for principal %s, cant set context: %s\n",
307 user, dce_errstr));
309 sec_login_purge_context(&my_dce_sec_context, &err);
310 goto err;
313 sec_login_get_pwent(my_dce_sec_context,
314 (sec_login_passwd_t *) & pw, &err);
315 if (err != error_status_ok)
317 dce_error_inq_text(err, dce_errstr, &err2);
318 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
319 goto err;
322 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
323 user, sys_getpid()));
325 DEBUG(3, ("DCE principal: %s\n"
326 " uid: %d\n"
327 " gid: %d\n",
328 pw->pw_name, pw->pw_uid, pw->pw_gid));
329 DEBUG(3, (" info: %s\n"
330 " dir: %s\n"
331 " shell: %s\n",
332 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
334 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
335 if (err != error_status_ok)
337 dce_error_inq_text(err, dce_errstr, &err2);
338 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
339 goto err;
342 set_effective_uid(0);
343 set_effective_gid(0);
345 t = localtime(&expire_time);
346 if (t) {
347 const char *asct = asctime(t);
348 if (asct) {
349 DEBUG(0,("DCE context expires: %s", asct));
353 dcelogin_atmost_once = 1;
354 return (True);
356 err:
358 /* Go back to root, JRA. */
359 set_effective_uid(0);
360 set_effective_gid(egid);
361 return (False);
364 void dfs_unlogin(void)
366 error_status_t err;
367 int err2;
368 unsigned char dce_errstr[dce_c_error_string_len];
370 sec_login_purge_context(&my_dce_sec_context, &err);
371 if (err != error_status_ok)
373 dce_error_inq_text(err, dce_errstr, &err2);
374 DEBUG(0,
375 ("DCE purge login context failed for server instance %d: %s\n",
376 sys_getpid(), dce_errstr));
379 #endif
381 #ifdef LINUX_BIGCRYPT
382 /****************************************************************************
383 an enhanced crypt for Linux to handle password longer than 8 characters
384 ****************************************************************************/
385 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
387 #define LINUX_PASSWORD_SEG_CHARS 8
388 char salt[3];
389 int i;
391 StrnCpy(salt, salt1, 2);
392 crypted += 2;
394 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
395 char *p = crypt(password, salt) + 2;
396 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
397 return (0);
398 password += LINUX_PASSWORD_SEG_CHARS;
399 crypted += strlen(p);
402 return (1);
404 #endif
406 #ifdef OSF1_ENH_SEC
407 /****************************************************************************
408 an enhanced crypt for OSF1
409 ****************************************************************************/
410 static char *osf1_bigcrypt(char *password, char *salt1)
412 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
413 char *p1;
414 char *p2 = password;
415 char salt[3];
416 int i;
417 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
418 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
419 parts++;
421 StrnCpy(salt, salt1, 2);
422 StrnCpy(result, salt1, 2);
423 result[2] = '\0';
425 for (i = 0; i < parts; i++) {
426 p1 = crypt(p2, salt);
427 strncat(result, p1 + 2,
428 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
429 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
430 p2 += AUTH_CLEARTEXT_SEG_CHARS;
433 return (result);
435 #endif
438 /****************************************************************************
439 apply a function to upper/lower case combinations
440 of a string and return true if one of them returns true.
441 try all combinations with N uppercase letters.
442 offset is the first char to try and change (start with 0)
443 it assumes the string starts lowercased
444 ****************************************************************************/
445 static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
446 int N)
448 int len = strlen(s);
449 int i;
450 NTSTATUS nt_status;
452 #ifdef PASSWORD_LENGTH
453 len = MIN(len, PASSWORD_LENGTH);
454 #endif
456 if (N <= 0 || offset >= len)
457 return (fn(s));
459 for (i = offset; i < (len - (N - 1)); i++) {
460 char c = s[i];
461 if (!islower_ascii(c))
462 continue;
463 s[i] = toupper_ascii(c);
464 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
465 return (nt_status);
467 s[i] = c;
469 return (NT_STATUS_WRONG_PASSWORD);
472 /****************************************************************************
473 apply a function to upper/lower case combinations
474 of a string and return true if one of them returns true.
475 try all combinations with up to N uppercase letters.
476 offset is the first char to try and change (start with 0)
477 it assumes the string starts lowercased
478 ****************************************************************************/
479 static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
481 int n;
482 NTSTATUS nt_status;
483 for (n = 1; n <= N; n++)
484 if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
485 return nt_status;
486 return NT_STATUS_WRONG_PASSWORD;
490 /****************************************************************************
491 core of password checking routine
492 ****************************************************************************/
493 static NTSTATUS password_check(const char *password)
495 #ifdef WITH_PAM
496 return smb_pam_passcheck(this_user, password);
497 #else
499 BOOL ret;
501 #ifdef WITH_AFS
502 if (afs_auth(this_user, password))
503 return NT_STATUS_OK;
504 #endif /* WITH_AFS */
506 #ifdef WITH_DFS
507 if (dfs_auth(this_user, password))
508 return NT_STATUS_OK;
509 #endif /* WITH_DFS */
511 #ifdef OSF1_ENH_SEC
513 ret = (strcmp(osf1_bigcrypt(password, this_salt),
514 this_crypted) == 0);
515 if (!ret) {
516 DEBUG(2,
517 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
518 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
520 if (ret) {
521 return NT_STATUS_OK;
522 } else {
523 return NT_STATUS_WRONG_PASSWORD;
526 #endif /* OSF1_ENH_SEC */
528 #ifdef ULTRIX_AUTH
529 ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
530 if (ret) {
531 return NT_STATUS_OK;
532 } else {
533 return NT_STATUS_WRONG_PASSWORD;
536 #endif /* ULTRIX_AUTH */
538 #ifdef LINUX_BIGCRYPT
539 ret = (linux_bigcrypt(password, this_salt, this_crypted));
540 if (ret) {
541 return NT_STATUS_OK;
542 } else {
543 return NT_STATUS_WRONG_PASSWORD;
545 #endif /* LINUX_BIGCRYPT */
547 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
550 * Some systems have bigcrypt in the C library but might not
551 * actually use it for the password hashes (HPUX 10.20) is
552 * a noteable example. So we try bigcrypt first, followed
553 * by crypt.
556 if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
557 return NT_STATUS_OK;
558 else
559 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
560 if (ret) {
561 return NT_STATUS_OK;
562 } else {
563 return NT_STATUS_WRONG_PASSWORD;
565 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
567 #ifdef HAVE_BIGCRYPT
568 ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
569 if (ret) {
570 return NT_STATUS_OK;
571 } else {
572 return NT_STATUS_WRONG_PASSWORD;
574 #endif /* HAVE_BIGCRYPT */
576 #ifndef HAVE_CRYPT
577 DEBUG(1, ("Warning - no crypt available\n"));
578 return NT_STATUS_LOGON_FAILURE;
579 #else /* HAVE_CRYPT */
580 ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
581 if (ret) {
582 return NT_STATUS_OK;
583 } else {
584 return NT_STATUS_WRONG_PASSWORD;
586 #endif /* HAVE_CRYPT */
587 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
588 #endif /* WITH_PAM */
593 /****************************************************************************
594 CHECK if a username/password is OK
595 the function pointer fn() points to a function to call when a successful
596 match is found and is used to update the encrypted password file
597 return NT_STATUS_OK on correct match, appropriate error otherwise
598 ****************************************************************************/
600 NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password,
601 int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
603 pstring pass2;
604 int level = lp_passwordlevel();
606 NTSTATUS nt_status;
608 #ifdef DEBUG_PASSWORD
609 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
610 #endif
612 if (!password)
613 return NT_STATUS_LOGON_FAILURE;
615 if (((!*password) || (!pwlen)) && !lp_null_passwords())
616 return NT_STATUS_LOGON_FAILURE;
618 #if defined(WITH_PAM)
621 * If we're using PAM we want to short-circuit all the
622 * checks below and dive straight into the PAM code.
625 fstrcpy(this_user, user);
627 DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
629 #else /* Not using PAM */
631 DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
633 if (!pass) {
634 DEBUG(3, ("Couldn't find user %s\n", user));
635 return NT_STATUS_NO_SUCH_USER;
639 /* Copy into global for the convenience of looping code */
640 /* Also the place to keep the 'password' no matter what
641 crazy struct it started in... */
642 fstrcpy(this_crypted, pass->pw_passwd);
643 fstrcpy(this_salt, pass->pw_passwd);
645 #ifdef HAVE_GETSPNAM
647 struct spwd *spass;
649 /* many shadow systems require you to be root to get
650 the password, in most cases this should already be
651 the case when this function is called, except
652 perhaps for IPC password changing requests */
654 spass = getspnam(pass->pw_name);
655 if (spass && spass->sp_pwdp) {
656 fstrcpy(this_crypted, spass->sp_pwdp);
657 fstrcpy(this_salt, spass->sp_pwdp);
660 #elif defined(IA_UINFO)
662 /* Need to get password with SVR4.2's ia_ functions
663 instead of get{sp,pw}ent functions. Required by
664 UnixWare 2.x, tested on version
665 2.1. (tangent@cyberport.com) */
666 uinfo_t uinfo;
667 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
668 ia_get_logpwd(uinfo, &(pass->pw_passwd));
670 #endif
672 #ifdef HAVE_GETPRPWNAM
674 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
675 if (pr_pw && pr_pw->ufld.fd_encrypt)
676 fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
678 #endif
680 #ifdef HAVE_GETPWANAM
682 struct passwd_adjunct *pwret;
683 pwret = getpwanam(s);
684 if (pwret && pwret->pwa_passwd)
685 fstrcpy(this_crypted, pwret->pwa_passwd);
687 #endif
689 #ifdef OSF1_ENH_SEC
691 struct pr_passwd *mypasswd;
692 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
693 user));
694 mypasswd = getprpwnam(user);
695 if (mypasswd) {
696 fstrcpy(this_user, mypasswd->ufld.fd_name);
697 fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
698 } else {
699 DEBUG(5,
700 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
701 user));
704 #endif
706 #ifdef ULTRIX_AUTH
708 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
709 if (ap) {
710 fstrcpy(this_crypted, ap->a_password);
711 endauthent();
714 #endif
716 #if defined(HAVE_TRUNCATED_SALT)
717 /* crypt on some platforms (HPUX in particular)
718 won't work with more than 2 salt characters. */
719 this_salt[2] = 0;
720 #endif
722 if (!*this_crypted) {
723 if (!lp_null_passwords()) {
724 DEBUG(2, ("Disallowing %s with null password\n",
725 this_user));
726 return NT_STATUS_LOGON_FAILURE;
728 if (!*password) {
729 DEBUG(3,
730 ("Allowing access to %s with null password\n",
731 this_user));
732 return NT_STATUS_OK;
736 #endif /* defined(WITH_PAM) */
738 /* try it as it came to us */
739 nt_status = password_check(password);
740 if NT_STATUS_IS_OK(nt_status) {
741 if (fn) {
742 fn(user, password);
744 return (nt_status);
745 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
746 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
747 return (nt_status);
750 if (!run_cracker) {
751 return (nt_status);
754 /* if the password was given to us with mixed case then we don't
755 * need to proceed as we know it hasn't been case modified by the
756 * client */
757 if (strhasupper(password) && strhaslower(password)) {
758 return nt_status;
761 /* make a copy of it */
762 pstrcpy(pass2, password);
764 /* try all lowercase if it's currently all uppercase */
765 if (strhasupper(pass2)) {
766 strlower_m(pass2);
767 if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
768 if (fn)
769 fn(user, pass2);
770 return (nt_status);
774 /* give up? */
775 if (level < 1) {
776 return NT_STATUS_WRONG_PASSWORD;
779 /* last chance - all combinations of up to level chars upper! */
780 strlower_m(pass2);
782 if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
783 if (fn)
784 fn(user, pass2);
785 return nt_status;
788 return NT_STATUS_WRONG_PASSWORD;