2941bc07f81d1c6d679e7369367b7ec7e18d93ea
[samba.git] / source3 / auth / pass_check.c
1 /*
2    Unix SMB/CIFS implementation.
3    Password checking
4    Copyright (C) Andrew Tridgell 1992-1998
5
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.
10
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.
15
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/>.
18 */
19
20 /* this module is for checking a username/password against a system
21    password database. The SMB encrypted password support is elsewhere */
22
23 #include "includes.h"
24 #include "system/passwd.h"
25 #include "auth.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_AUTH
29
30 #if !defined(WITH_PAM)
31 static char *ths_salt;
32 /* This must be writable. */
33 static char *get_this_salt(void)
34 {
35         return ths_salt;
36 }
37
38 /* We may be setting a modified version of the same
39  * string, so don't free before use. */
40
41 static const char *set_this_salt(const char *newsalt)
42 {
43         char *orig_salt = ths_salt;
44         ths_salt = SMB_STRDUP(newsalt);
45         SAFE_FREE(orig_salt);
46         return ths_salt;
47 }
48
49 static char *ths_crypted;
50 static const char *get_this_crypted(void)
51 {
52         if (!ths_crypted) {
53                 return "";
54         }
55         return ths_crypted;
56 }
57
58 static const char *set_this_crypted(const char *newcrypted)
59 {
60         char *orig_crypted = ths_crypted;
61         ths_crypted = SMB_STRDUP(newcrypted);
62         SAFE_FREE(orig_crypted);
63         return ths_crypted;
64 }
65 #endif
66
67
68
69
70
71
72
73 /****************************************************************************
74 core of password checking routine
75 ****************************************************************************/
76 static NTSTATUS password_check(const char *user, const char *password, const void *private_data)
77 {
78 #ifdef WITH_PAM
79         const char *rhost = (const char *)private_data;
80         return smb_pam_passcheck(user, rhost, password);
81 #else
82
83         bool ret;
84
85
86
87
88 #ifdef ULTRIX_AUTH
89         ret = (strcmp((char *)crypt16(password, get_this_salt()), get_this_crypted()) == 0);
90         if (ret) {
91                 return NT_STATUS_OK;
92         } else {
93                 return NT_STATUS_WRONG_PASSWORD;
94         }
95
96 #endif /* ULTRIX_AUTH */
97
98
99 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
100
101         /*
102          * Some systems have bigcrypt in the C library but might not
103          * actually use it for the password hashes (HPUX 10.20) is
104          * a noteable example. So we try bigcrypt first, followed
105          * by crypt.
106          */
107
108         if (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0)
109                 return NT_STATUS_OK;
110         else
111                 ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
112         if (ret) {
113                 return NT_STATUS_OK;
114         } else {
115                 return NT_STATUS_WRONG_PASSWORD;
116         }
117 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
118
119 #ifdef HAVE_BIGCRYPT
120         ret = (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0);
121         if (ret) {
122                 return NT_STATUS_OK;
123         } else {
124                 return NT_STATUS_WRONG_PASSWORD;
125         }
126 #endif /* HAVE_BIGCRYPT */
127
128 #ifndef HAVE_CRYPT
129         DEBUG(1, ("Warning - no crypt available\n"));
130         return NT_STATUS_LOGON_FAILURE;
131 #else /* HAVE_CRYPT */
132         ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
133         if (ret) {
134                 return NT_STATUS_OK;
135         } else {
136                 return NT_STATUS_WRONG_PASSWORD;
137         }
138 #endif /* HAVE_CRYPT */
139 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
140 #endif /* WITH_PAM */
141 }
142
143
144
145 /****************************************************************************
146 CHECK if a username/password is OK
147 the function pointer fn() points to a function to call when a successful
148 match is found and is used to update the encrypted password file 
149 return NT_STATUS_OK on correct match, appropriate error otherwise
150 ****************************************************************************/
151
152 NTSTATUS pass_check(const struct passwd *pass,
153                     const char *user,
154                     const char *rhost,
155                     const char *password,
156                     bool run_cracker)
157 {
158         char *pass2 = NULL;
159
160         NTSTATUS nt_status;
161
162 #ifdef DEBUG_PASSWORD
163         DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
164 #endif
165
166         if (!password)
167                 return NT_STATUS_LOGON_FAILURE;
168
169         if ((!*password) && !lp_null_passwords())
170                 return NT_STATUS_LOGON_FAILURE;
171
172 #if defined(WITH_PAM) 
173
174         /*
175          * If we're using PAM we want to short-circuit all the 
176          * checks below and dive straight into the PAM code.
177          */
178
179         DEBUG(4, ("pass_check: Checking (PAM) password for user %s\n", user));
180
181 #else /* Not using PAM */
182
183         DEBUG(4, ("pass_check: Checking password for user %s\n", user));
184
185         if (!pass) {
186                 DEBUG(3, ("Couldn't find user %s\n", user));
187                 return NT_STATUS_NO_SUCH_USER;
188         }
189
190
191         /* Copy into global for the convenience of looping code */
192         /* Also the place to keep the 'password' no matter what
193            crazy struct it started in... */
194         if (set_this_crypted(pass->pw_passwd) == NULL) {
195                 return NT_STATUS_NO_MEMORY;
196         }
197         if (set_this_salt(pass->pw_passwd) == NULL) {
198                 return NT_STATUS_NO_MEMORY;
199         }
200
201 #ifdef HAVE_GETSPNAM
202         {
203                 struct spwd *spass;
204
205                 /* many shadow systems require you to be root to get
206                    the password, in most cases this should already be
207                    the case when this function is called, except
208                    perhaps for IPC password changing requests */
209
210                 spass = getspnam(pass->pw_name);
211                 if (spass && spass->sp_pwdp) {
212                         if (set_this_crypted(spass->sp_pwdp) == NULL) {
213                                 return NT_STATUS_NO_MEMORY;
214                         }
215                         if (set_this_salt(spass->sp_pwdp) == NULL) {
216                                 return NT_STATUS_NO_MEMORY;
217                         }
218                 }
219         }
220 #elif defined(IA_UINFO)
221         {
222                 /* Need to get password with SVR4.2's ia_ functions
223                    instead of get{sp,pw}ent functions. Required by
224                    UnixWare 2.x, tested on version
225                    2.1. (tangent@cyberport.com) */
226                 uinfo_t uinfo;
227                 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
228                         ia_get_logpwd(uinfo, &(pass->pw_passwd));
229         }
230 #endif
231
232
233 #ifdef HAVE_GETPWANAM
234         {
235                 struct passwd_adjunct *pwret;
236                 pwret = getpwanam(s);
237                 if (pwret && pwret->pwa_passwd) {
238                         if (set_this_crypted(pwret->pwa_passwd) == NULL) {
239                                 return NT_STATUS_NO_MEMORY;
240                         }
241                 }
242         }
243 #endif
244
245
246 #ifdef ULTRIX_AUTH
247         {
248                 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
249                 if (ap) {
250                         if (set_this_crypted(ap->a_password) == NULL) {
251                                 endauthent();
252                                 return NT_STATUS_NO_MEMORY;
253                         }
254                         endauthent();
255                 }
256         }
257 #endif
258
259 #if defined(HAVE_TRUNCATED_SALT)
260         /* crypt on some platforms (HPUX in particular)
261            won't work with more than 2 salt characters. */
262         {
263                 char *trunc_salt = get_this_salt();
264                 if (!trunc_salt || strlen(trunc_salt) < 2) {
265                         return NT_STATUS_LOGON_FAILURE;
266                 }
267                 trunc_salt[2] = 0;
268                 if (set_this_salt(trunc_salt) == NULL) {
269                         return NT_STATUS_NO_MEMORY;
270                 }
271         }
272 #endif
273
274         if (!get_this_crypted() || !*get_this_crypted()) {
275                 if (!lp_null_passwords()) {
276                         DEBUG(2, ("Disallowing %s with null password\n",
277                                   user));
278                         return NT_STATUS_LOGON_FAILURE;
279                 }
280                 if (!*password) {
281                         DEBUG(3,
282                               ("Allowing access to %s with null password\n",
283                                user));
284                         return NT_STATUS_OK;
285                 }
286         }
287
288 #endif /* defined(WITH_PAM) */
289
290         /* try it as it came to us */
291         nt_status = password_check(user, password, (const void *)rhost);
292         if NT_STATUS_IS_OK(nt_status) {
293                 return (nt_status);
294         } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
295                 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
296                 return (nt_status);
297         }
298
299         if (!run_cracker) {
300                 return (nt_status);
301         }
302
303         /* if the password was given to us with mixed case then we don't
304          * need to proceed as we know it hasn't been case modified by the
305          * client */
306         if (strhasupper(password) && strhaslower(password)) {
307                 return nt_status;
308         }
309
310         /* make a copy of it */
311         pass2 = talloc_strdup(talloc_tos(), password);
312         if (!pass2) {
313                 return NT_STATUS_NO_MEMORY;
314         }
315
316         /* try all lowercase if it's currently all uppercase */
317         if (strhasupper(pass2)) {
318                 if (!strlower_m(pass2)) {
319                         return NT_STATUS_INVALID_PARAMETER;
320                 }
321                 nt_status = password_check(user, pass2, (const void *)rhost);
322                 if (NT_STATUS_IS_OK(nt_status)) {
323                         return (nt_status);
324                 }
325         }
326
327         return NT_STATUS_WRONG_PASSWORD;
328 }