libsmbclient: Fix typos
[Samba/id10ts.git] / source3 / auth / pass_check.c
blob21694b3d555c84aaac2ef09c9641e04f18d3e3da
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 3 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, see <http://www.gnu.org/licenses/>.
20 /* this module is for checking a username/password against a system
21 password database. The SMB encrypted password support is elsewhere */
23 #include "includes.h"
24 #include "system/passwd.h"
25 #include "auth.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_AUTH
30 /* what is the longest significant password available on your system?
31 Knowing this speeds up password searches a lot */
32 #ifndef PASSWORD_LENGTH
33 #define PASSWORD_LENGTH 8
34 #endif
36 /* these are kept here to keep the string_combinations function simple */
37 static char *ths_user;
39 static const char *get_this_user(void)
41 if (!ths_user) {
42 return "";
44 return ths_user;
47 #if defined(WITH_PAM) || defined(OSF1_ENH_SEC)
48 static const char *set_this_user(const char *newuser)
50 char *orig_user = ths_user;
51 ths_user = SMB_STRDUP(newuser);
52 SAFE_FREE(orig_user);
53 return ths_user;
55 #endif
57 #if !defined(WITH_PAM)
58 static char *ths_salt;
59 /* This must be writable. */
60 static char *get_this_salt(void)
62 return ths_salt;
65 /* We may be setting a modified version of the same
66 * string, so don't free before use. */
68 static const char *set_this_salt(const char *newsalt)
70 char *orig_salt = ths_salt;
71 ths_salt = SMB_STRDUP(newsalt);
72 SAFE_FREE(orig_salt);
73 return ths_salt;
76 static char *ths_crypted;
77 static const char *get_this_crypted(void)
79 if (!ths_crypted) {
80 return "";
82 return ths_crypted;
85 static const char *set_this_crypted(const char *newcrypted)
87 char *orig_crypted = ths_crypted;
88 ths_crypted = SMB_STRDUP(newcrypted);
89 SAFE_FREE(orig_crypted);
90 return ths_crypted;
92 #endif
94 #ifdef WITH_AFS
96 #include <afs/stds.h>
97 #include <afs/kautils.h>
99 /*******************************************************************
100 check on AFS authentication
101 ********************************************************************/
102 static bool afs_auth(char *user, char *password)
104 long password_expires = 0;
105 char *reason;
107 /* For versions of AFS prior to 3.3, this routine has few arguments, */
108 /* but since I can't find the old documentation... :-) */
109 setpag();
110 if (ka_UserAuthenticateGeneral
111 (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
112 (char *)0, /* cell */
113 password, 0, /* lifetime, default */
114 &password_expires, /*days 'til it expires */
115 0, /* spare 2 */
116 &reason) == 0)
118 return (True);
120 DEBUG(1,
121 ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
122 return (False);
124 #endif
127 #ifdef WITH_DFS
129 #include <dce/dce_error.h>
130 #include <dce/sec_login.h>
132 /*****************************************************************
133 This new version of the DFS_AUTH code was donated by Karsten Muuss
134 <muuss@or.uni-bonn.de>. It fixes the following problems with the
135 old code :
137 - Server credentials may expire
138 - Client credential cache files have wrong owner
139 - purge_context() function is called with invalid argument
141 This new code was modified to ensure that on exit the uid/gid is
142 still root, and the original directory is restored. JRA.
143 ******************************************************************/
145 sec_login_handle_t my_dce_sec_context;
146 int dcelogin_atmost_once = 0;
148 /*******************************************************************
149 check on a DCE/DFS authentication
150 ********************************************************************/
151 static bool dfs_auth(char *user, char *password)
153 struct tm *t;
154 error_status_t err;
155 int err2;
156 int prterr;
157 signed32 expire_time, current_time;
158 boolean32 password_reset;
159 struct passwd *pw;
160 sec_passwd_rec_t passwd_rec;
161 sec_login_auth_src_t auth_src = sec_login_auth_src_network;
162 unsigned char dce_errstr[dce_c_error_string_len];
163 gid_t egid;
165 if (dcelogin_atmost_once)
166 return (False);
168 #ifdef HAVE_CRYPT
170 * We only go for a DCE login context if the given password
171 * matches that stored in the local password file..
172 * Assumes local passwd file is kept in sync w/ DCE RGY!
175 if (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()))
177 return (False);
179 #endif
181 sec_login_get_current_context(&my_dce_sec_context, &err);
182 if (err != error_status_ok)
184 dce_error_inq_text(err, dce_errstr, &err2);
185 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
187 return (False);
190 sec_login_certify_identity(my_dce_sec_context, &err);
191 if (err != error_status_ok)
193 dce_error_inq_text(err, dce_errstr, &err2);
194 DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
196 return (False);
199 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
200 if (err != error_status_ok)
202 dce_error_inq_text(err, dce_errstr, &err2);
203 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
205 return (False);
208 time(&current_time);
210 if (expire_time < (current_time + 60))
212 struct passwd *pw;
213 sec_passwd_rec_t *key;
215 sec_login_get_pwent(my_dce_sec_context,
216 (sec_login_passwd_t *) & pw, &err);
217 if (err != error_status_ok)
219 dce_error_inq_text(err, dce_errstr, &err2);
220 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
222 return (False);
225 sec_login_refresh_identity(my_dce_sec_context, &err);
226 if (err != error_status_ok)
228 dce_error_inq_text(err, dce_errstr, &err2);
229 DEBUG(0, ("DCE can't refresh identity. %s\n",
230 dce_errstr));
232 return (False);
235 sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
236 (unsigned char *)pw->pw_name,
237 sec_c_key_version_none,
238 (void **)&key, &err);
239 if (err != error_status_ok)
241 dce_error_inq_text(err, dce_errstr, &err2);
242 DEBUG(0, ("DCE can't get key for %s. %s\n",
243 pw->pw_name, dce_errstr));
245 return (False);
248 sec_login_valid_and_cert_ident(my_dce_sec_context, key,
249 &password_reset, &auth_src,
250 &err);
251 if (err != error_status_ok)
253 dce_error_inq_text(err, dce_errstr, &err2);
254 DEBUG(0,
255 ("DCE can't validate and certify identity for %s. %s\n",
256 pw->pw_name, dce_errstr));
259 sec_key_mgmt_free_key(key, &err);
260 if (err != error_status_ok)
262 dce_error_inq_text(err, dce_errstr, &err2);
263 DEBUG(0, ("DCE can't free key.\n", dce_errstr));
267 if (sec_login_setup_identity((unsigned char *)user,
268 sec_login_no_flags,
269 &my_dce_sec_context, &err) == 0)
271 dce_error_inq_text(err, dce_errstr, &err2);
272 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
273 user, dce_errstr));
274 return (False);
277 sec_login_get_pwent(my_dce_sec_context,
278 (sec_login_passwd_t *) & pw, &err);
279 if (err != error_status_ok)
281 dce_error_inq_text(err, dce_errstr, &err2);
282 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
284 return (False);
287 sec_login_purge_context(&my_dce_sec_context, &err);
288 if (err != error_status_ok)
290 dce_error_inq_text(err, dce_errstr, &err2);
291 DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
293 return (False);
297 * NB. I'd like to change these to call something like change_to_user()
298 * instead but currently we don't have a connection
299 * context to become the correct user. This is already
300 * fairly platform specific code however, so I think
301 * this should be ok. I have added code to go
302 * back to being root on error though. JRA.
305 egid = getegid();
307 set_effective_gid(pw->pw_gid);
308 set_effective_uid(pw->pw_uid);
310 if (sec_login_setup_identity((unsigned char *)user,
311 sec_login_no_flags,
312 &my_dce_sec_context, &err) == 0)
314 dce_error_inq_text(err, dce_errstr, &err2);
315 DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
316 user, dce_errstr));
317 goto err;
320 sec_login_get_pwent(my_dce_sec_context,
321 (sec_login_passwd_t *) & pw, &err);
322 if (err != error_status_ok)
324 dce_error_inq_text(err, dce_errstr, &err2);
325 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
326 goto err;
329 passwd_rec.version_number = sec_passwd_c_version_none;
330 passwd_rec.pepper = NULL;
331 passwd_rec.key.key_type = sec_passwd_plain;
332 passwd_rec.key.tagged_union.plain = (idl_char *) password;
334 sec_login_validate_identity(my_dce_sec_context,
335 &passwd_rec, &password_reset,
336 &auth_src, &err);
337 if (err != error_status_ok)
339 dce_error_inq_text(err, dce_errstr, &err2);
340 DEBUG(0,
341 ("DCE Identity Validation failed for principal %s: %s\n",
342 user, dce_errstr));
343 goto err;
346 sec_login_certify_identity(my_dce_sec_context, &err);
347 if (err != error_status_ok)
349 dce_error_inq_text(err, dce_errstr, &err2);
350 DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
351 goto err;
354 if (auth_src != sec_login_auth_src_network)
356 DEBUG(0, ("DCE context has no network credentials.\n"));
359 sec_login_set_context(my_dce_sec_context, &err);
360 if (err != error_status_ok)
362 dce_error_inq_text(err, dce_errstr, &err2);
363 DEBUG(0,
364 ("DCE login failed for principal %s, cant set context: %s\n",
365 user, dce_errstr));
367 sec_login_purge_context(&my_dce_sec_context, &err);
368 goto err;
371 sec_login_get_pwent(my_dce_sec_context,
372 (sec_login_passwd_t *) & pw, &err);
373 if (err != error_status_ok)
375 dce_error_inq_text(err, dce_errstr, &err2);
376 DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
377 goto err;
380 DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
381 user, getpid()));
383 DEBUG(3, ("DCE principal: %s\n"
384 " uid: %d\n"
385 " gid: %d\n",
386 pw->pw_name, pw->pw_uid, pw->pw_gid));
387 DEBUG(3, (" info: %s\n"
388 " dir: %s\n"
389 " shell: %s\n",
390 pw->pw_gecos, pw->pw_dir, pw->pw_shell));
392 sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
393 if (err != error_status_ok)
395 dce_error_inq_text(err, dce_errstr, &err2);
396 DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
397 goto err;
400 set_effective_uid(0);
401 set_effective_gid(0);
403 t = localtime(&expire_time);
404 if (t) {
405 const char *asct = asctime(t);
406 if (asct) {
407 DEBUG(0,("DCE context expires: %s", asct));
411 dcelogin_atmost_once = 1;
412 return (True);
414 err:
416 /* Go back to root, JRA. */
417 set_effective_uid(0);
418 set_effective_gid(egid);
419 return (False);
422 void dfs_unlogin(void)
424 error_status_t err;
425 int err2;
426 unsigned char dce_errstr[dce_c_error_string_len];
428 sec_login_purge_context(&my_dce_sec_context, &err);
429 if (err != error_status_ok)
431 dce_error_inq_text(err, dce_errstr, &err2);
432 DEBUG(0,
433 ("DCE purge login context failed for server instance %d: %s\n",
434 getpid(), dce_errstr));
437 #endif
439 #ifdef LINUX_BIGCRYPT
440 /****************************************************************************
441 an enhanced crypt for Linux to handle password longer than 8 characters
442 ****************************************************************************/
443 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
445 #define LINUX_PASSWORD_SEG_CHARS 8
446 char salt[3];
447 int i;
449 StrnCpy(salt, salt1, 2);
450 crypted += 2;
452 for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
453 char *p = crypt(password, salt) + 2;
454 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
455 return (0);
456 password += LINUX_PASSWORD_SEG_CHARS;
457 crypted += strlen(p);
460 return (1);
462 #endif
464 #ifdef OSF1_ENH_SEC
465 /****************************************************************************
466 an enhanced crypt for OSF1
467 ****************************************************************************/
468 static char *osf1_bigcrypt(char *password, char *salt1)
470 static char result[AUTH_MAX_PASSWD_LENGTH] = "";
471 char *p1;
472 char *p2 = password;
473 char salt[3];
474 int i;
475 int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
476 if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
477 parts++;
479 StrnCpy(salt, salt1, 2);
480 StrnCpy(result, salt1, 2);
481 result[2] = '\0';
483 for (i = 0; i < parts; i++) {
484 p1 = crypt(p2, salt);
485 strncat(result, p1 + 2,
486 AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
487 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
488 p2 += AUTH_CLEARTEXT_SEG_CHARS;
491 return (result);
493 #endif
496 /****************************************************************************
497 core of password checking routine
498 ****************************************************************************/
499 static NTSTATUS password_check(const char *password, const void *private_data)
501 #ifdef WITH_PAM
502 const char *rhost = (const char *)private_data;
503 return smb_pam_passcheck(get_this_user(), rhost, password);
504 #else
506 bool ret;
508 #ifdef WITH_AFS
509 if (afs_auth(get_this_user(), password))
510 return NT_STATUS_OK;
511 #endif /* WITH_AFS */
513 #ifdef WITH_DFS
514 if (dfs_auth(get_this_user(), password))
515 return NT_STATUS_OK;
516 #endif /* WITH_DFS */
518 #ifdef OSF1_ENH_SEC
520 ret = (strcmp(osf1_bigcrypt(password, get_this_salt()),
521 get_this_crypted()) == 0);
522 if (!ret) {
523 DEBUG(2,
524 ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
525 ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
527 if (ret) {
528 return NT_STATUS_OK;
529 } else {
530 return NT_STATUS_WRONG_PASSWORD;
533 #endif /* OSF1_ENH_SEC */
535 #ifdef ULTRIX_AUTH
536 ret = (strcmp((char *)crypt16(password, get_this_salt()), get_this_crypted()) == 0);
537 if (ret) {
538 return NT_STATUS_OK;
539 } else {
540 return NT_STATUS_WRONG_PASSWORD;
543 #endif /* ULTRIX_AUTH */
545 #ifdef LINUX_BIGCRYPT
546 ret = (linux_bigcrypt(password, get_this_salt(), get_this_crypted()));
547 if (ret) {
548 return NT_STATUS_OK;
549 } else {
550 return NT_STATUS_WRONG_PASSWORD;
552 #endif /* LINUX_BIGCRYPT */
554 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
557 * Some systems have bigcrypt in the C library but might not
558 * actually use it for the password hashes (HPUX 10.20) is
559 * a noteable example. So we try bigcrypt first, followed
560 * by crypt.
563 if (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0)
564 return NT_STATUS_OK;
565 else
566 ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
567 if (ret) {
568 return NT_STATUS_OK;
569 } else {
570 return NT_STATUS_WRONG_PASSWORD;
572 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
574 #ifdef HAVE_BIGCRYPT
575 ret = (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0);
576 if (ret) {
577 return NT_STATUS_OK;
578 } else {
579 return NT_STATUS_WRONG_PASSWORD;
581 #endif /* HAVE_BIGCRYPT */
583 #ifndef HAVE_CRYPT
584 DEBUG(1, ("Warning - no crypt available\n"));
585 return NT_STATUS_LOGON_FAILURE;
586 #else /* HAVE_CRYPT */
587 ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
588 if (ret) {
589 return NT_STATUS_OK;
590 } else {
591 return NT_STATUS_WRONG_PASSWORD;
593 #endif /* HAVE_CRYPT */
594 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
595 #endif /* WITH_PAM */
600 /****************************************************************************
601 CHECK if a username/password is OK
602 the function pointer fn() points to a function to call when a successful
603 match is found and is used to update the encrypted password file
604 return NT_STATUS_OK on correct match, appropriate error otherwise
605 ****************************************************************************/
607 NTSTATUS pass_check(const struct passwd *pass,
608 const char *user,
609 const char *rhost,
610 const char *password,
611 bool run_cracker)
613 char *pass2 = NULL;
615 NTSTATUS nt_status;
617 #ifdef DEBUG_PASSWORD
618 DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
619 #endif
621 if (!password)
622 return NT_STATUS_LOGON_FAILURE;
624 if ((!*password) && !lp_null_passwords())
625 return NT_STATUS_LOGON_FAILURE;
627 #if defined(WITH_PAM)
630 * If we're using PAM we want to short-circuit all the
631 * checks below and dive straight into the PAM code.
634 if (set_this_user(user) == NULL) {
635 return NT_STATUS_NO_MEMORY;
638 DEBUG(4, ("pass_check: Checking (PAM) password for user %s\n", user));
640 #else /* Not using PAM */
642 DEBUG(4, ("pass_check: Checking password for user %s\n", user));
644 if (!pass) {
645 DEBUG(3, ("Couldn't find user %s\n", user));
646 return NT_STATUS_NO_SUCH_USER;
650 /* Copy into global for the convenience of looping code */
651 /* Also the place to keep the 'password' no matter what
652 crazy struct it started in... */
653 if (set_this_crypted(pass->pw_passwd) == NULL) {
654 return NT_STATUS_NO_MEMORY;
656 if (set_this_salt(pass->pw_passwd) == NULL) {
657 return NT_STATUS_NO_MEMORY;
660 #ifdef HAVE_GETSPNAM
662 struct spwd *spass;
664 /* many shadow systems require you to be root to get
665 the password, in most cases this should already be
666 the case when this function is called, except
667 perhaps for IPC password changing requests */
669 spass = getspnam(pass->pw_name);
670 if (spass && spass->sp_pwdp) {
671 if (set_this_crypted(spass->sp_pwdp) == NULL) {
672 return NT_STATUS_NO_MEMORY;
674 if (set_this_salt(spass->sp_pwdp) == NULL) {
675 return NT_STATUS_NO_MEMORY;
679 #elif defined(IA_UINFO)
681 /* Need to get password with SVR4.2's ia_ functions
682 instead of get{sp,pw}ent functions. Required by
683 UnixWare 2.x, tested on version
684 2.1. (tangent@cyberport.com) */
685 uinfo_t uinfo;
686 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
687 ia_get_logpwd(uinfo, &(pass->pw_passwd));
689 #endif
691 #ifdef HAVE_GETPRPWNAM
693 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
694 if (pr_pw && pr_pw->ufld.fd_encrypt) {
695 if (set_this_crypted(pr_pw->ufld.fd_encrypt) == NULL) {
696 return NT_STATUS_NO_MEMORY;
700 #endif
702 #ifdef HAVE_GETPWANAM
704 struct passwd_adjunct *pwret;
705 pwret = getpwanam(s);
706 if (pwret && pwret->pwa_passwd) {
707 if (set_this_crypted(pwret->pwa_passwd) == NULL) {
708 return NT_STATUS_NO_MEMORY;
712 #endif
714 #ifdef OSF1_ENH_SEC
716 struct pr_passwd *mypasswd;
717 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
718 user));
719 mypasswd = getprpwnam(user);
720 if (mypasswd) {
721 if (set_this_user(mypasswd->ufld.fd_name) == NULL) {
722 return NT_STATUS_NO_MEMORY;
724 if (set_this_crypted(mypasswd->ufld.fd_encrypt) == NULL) {
725 return NT_STATUS_NO_MEMORY;
727 } else {
728 DEBUG(5,
729 ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
730 user));
733 #endif
735 #ifdef ULTRIX_AUTH
737 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
738 if (ap) {
739 if (set_this_crypted(ap->a_password) == NULL) {
740 endauthent();
741 return NT_STATUS_NO_MEMORY;
743 endauthent();
746 #endif
748 #if defined(HAVE_TRUNCATED_SALT)
749 /* crypt on some platforms (HPUX in particular)
750 won't work with more than 2 salt characters. */
752 char *trunc_salt = get_this_salt();
753 if (!trunc_salt || strlen(trunc_salt) < 2) {
754 return NT_STATUS_LOGON_FAILURE;
756 trunc_salt[2] = 0;
757 if (set_this_salt(trunc_salt) == NULL) {
758 return NT_STATUS_NO_MEMORY;
761 #endif
763 if (!get_this_crypted() || !*get_this_crypted()) {
764 if (!lp_null_passwords()) {
765 DEBUG(2, ("Disallowing %s with null password\n",
766 get_this_user()));
767 return NT_STATUS_LOGON_FAILURE;
769 if (!*password) {
770 DEBUG(3,
771 ("Allowing access to %s with null password\n",
772 get_this_user()));
773 return NT_STATUS_OK;
777 #endif /* defined(WITH_PAM) */
779 /* try it as it came to us */
780 nt_status = password_check(password, (const void *)rhost);
781 if NT_STATUS_IS_OK(nt_status) {
782 return (nt_status);
783 } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
784 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
785 return (nt_status);
788 if (!run_cracker) {
789 return (nt_status);
792 /* if the password was given to us with mixed case then we don't
793 * need to proceed as we know it hasn't been case modified by the
794 * client */
795 if (strhasupper(password) && strhaslower(password)) {
796 return nt_status;
799 /* make a copy of it */
800 pass2 = talloc_strdup(talloc_tos(), password);
801 if (!pass2) {
802 return NT_STATUS_NO_MEMORY;
805 /* try all lowercase if it's currently all uppercase */
806 if (strhasupper(pass2)) {
807 if (!strlower_m(pass2)) {
808 return NT_STATUS_INVALID_PARAMETER;
810 nt_status = password_check(pass2, (const void *)rhost);
811 if (NT_STATUS_IS_OK(nt_status)) {
812 return (nt_status);
816 return NT_STATUS_WRONG_PASSWORD;