a5e03202b88b7d5db2aadef42a5158915827e3c3
[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 #ifdef LINUX_BIGCRYPT
71 /****************************************************************************
72 an enhanced crypt for Linux to handle password longer than 8 characters
73 ****************************************************************************/
74 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
75 {
76 #define LINUX_PASSWORD_SEG_CHARS 8
77         char salt[3];
78         int i;
79
80         StrnCpy(salt, salt1, 2);
81         crypted += 2;
82
83         for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
84                 char *p = crypt(password, salt) + 2;
85                 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
86                         return (0);
87                 password += LINUX_PASSWORD_SEG_CHARS;
88                 crypted += strlen(p);
89         }
90
91         return (1);
92 }
93 #endif
94
95
96
97 /****************************************************************************
98 core of password checking routine
99 ****************************************************************************/
100 static NTSTATUS password_check(const char *user, const char *password, const void *private_data)
101 {
102 #ifdef WITH_PAM
103         const char *rhost = (const char *)private_data;
104         return smb_pam_passcheck(user, rhost, password);
105 #else
106
107         bool ret;
108
109
110
111
112 #ifdef ULTRIX_AUTH
113         ret = (strcmp((char *)crypt16(password, get_this_salt()), get_this_crypted()) == 0);
114         if (ret) {
115                 return NT_STATUS_OK;
116         } else {
117                 return NT_STATUS_WRONG_PASSWORD;
118         }
119
120 #endif /* ULTRIX_AUTH */
121
122 #ifdef LINUX_BIGCRYPT
123         ret = (linux_bigcrypt(password, get_this_salt(), get_this_crypted()));
124         if (ret) {
125                 return NT_STATUS_OK;
126         } else {
127                 return NT_STATUS_WRONG_PASSWORD;
128         }
129 #endif /* LINUX_BIGCRYPT */
130
131 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
132
133         /*
134          * Some systems have bigcrypt in the C library but might not
135          * actually use it for the password hashes (HPUX 10.20) is
136          * a noteable example. So we try bigcrypt first, followed
137          * by crypt.
138          */
139
140         if (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0)
141                 return NT_STATUS_OK;
142         else
143                 ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
144         if (ret) {
145                 return NT_STATUS_OK;
146         } else {
147                 return NT_STATUS_WRONG_PASSWORD;
148         }
149 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
150
151 #ifdef HAVE_BIGCRYPT
152         ret = (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0);
153         if (ret) {
154                 return NT_STATUS_OK;
155         } else {
156                 return NT_STATUS_WRONG_PASSWORD;
157         }
158 #endif /* HAVE_BIGCRYPT */
159
160 #ifndef HAVE_CRYPT
161         DEBUG(1, ("Warning - no crypt available\n"));
162         return NT_STATUS_LOGON_FAILURE;
163 #else /* HAVE_CRYPT */
164         ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
165         if (ret) {
166                 return NT_STATUS_OK;
167         } else {
168                 return NT_STATUS_WRONG_PASSWORD;
169         }
170 #endif /* HAVE_CRYPT */
171 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
172 #endif /* WITH_PAM */
173 }
174
175
176
177 /****************************************************************************
178 CHECK if a username/password is OK
179 the function pointer fn() points to a function to call when a successful
180 match is found and is used to update the encrypted password file 
181 return NT_STATUS_OK on correct match, appropriate error otherwise
182 ****************************************************************************/
183
184 NTSTATUS pass_check(const struct passwd *pass,
185                     const char *user,
186                     const char *rhost,
187                     const char *password,
188                     bool run_cracker)
189 {
190         char *pass2 = NULL;
191
192         NTSTATUS nt_status;
193
194 #ifdef DEBUG_PASSWORD
195         DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
196 #endif
197
198         if (!password)
199                 return NT_STATUS_LOGON_FAILURE;
200
201         if ((!*password) && !lp_null_passwords())
202                 return NT_STATUS_LOGON_FAILURE;
203
204 #if defined(WITH_PAM) 
205
206         /*
207          * If we're using PAM we want to short-circuit all the 
208          * checks below and dive straight into the PAM code.
209          */
210
211         DEBUG(4, ("pass_check: Checking (PAM) password for user %s\n", user));
212
213 #else /* Not using PAM */
214
215         DEBUG(4, ("pass_check: Checking password for user %s\n", user));
216
217         if (!pass) {
218                 DEBUG(3, ("Couldn't find user %s\n", user));
219                 return NT_STATUS_NO_SUCH_USER;
220         }
221
222
223         /* Copy into global for the convenience of looping code */
224         /* Also the place to keep the 'password' no matter what
225            crazy struct it started in... */
226         if (set_this_crypted(pass->pw_passwd) == NULL) {
227                 return NT_STATUS_NO_MEMORY;
228         }
229         if (set_this_salt(pass->pw_passwd) == NULL) {
230                 return NT_STATUS_NO_MEMORY;
231         }
232
233 #ifdef HAVE_GETSPNAM
234         {
235                 struct spwd *spass;
236
237                 /* many shadow systems require you to be root to get
238                    the password, in most cases this should already be
239                    the case when this function is called, except
240                    perhaps for IPC password changing requests */
241
242                 spass = getspnam(pass->pw_name);
243                 if (spass && spass->sp_pwdp) {
244                         if (set_this_crypted(spass->sp_pwdp) == NULL) {
245                                 return NT_STATUS_NO_MEMORY;
246                         }
247                         if (set_this_salt(spass->sp_pwdp) == NULL) {
248                                 return NT_STATUS_NO_MEMORY;
249                         }
250                 }
251         }
252 #elif defined(IA_UINFO)
253         {
254                 /* Need to get password with SVR4.2's ia_ functions
255                    instead of get{sp,pw}ent functions. Required by
256                    UnixWare 2.x, tested on version
257                    2.1. (tangent@cyberport.com) */
258                 uinfo_t uinfo;
259                 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
260                         ia_get_logpwd(uinfo, &(pass->pw_passwd));
261         }
262 #endif
263
264
265 #ifdef HAVE_GETPWANAM
266         {
267                 struct passwd_adjunct *pwret;
268                 pwret = getpwanam(s);
269                 if (pwret && pwret->pwa_passwd) {
270                         if (set_this_crypted(pwret->pwa_passwd) == NULL) {
271                                 return NT_STATUS_NO_MEMORY;
272                         }
273                 }
274         }
275 #endif
276
277
278 #ifdef ULTRIX_AUTH
279         {
280                 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
281                 if (ap) {
282                         if (set_this_crypted(ap->a_password) == NULL) {
283                                 endauthent();
284                                 return NT_STATUS_NO_MEMORY;
285                         }
286                         endauthent();
287                 }
288         }
289 #endif
290
291 #if defined(HAVE_TRUNCATED_SALT)
292         /* crypt on some platforms (HPUX in particular)
293            won't work with more than 2 salt characters. */
294         {
295                 char *trunc_salt = get_this_salt();
296                 if (!trunc_salt || strlen(trunc_salt) < 2) {
297                         return NT_STATUS_LOGON_FAILURE;
298                 }
299                 trunc_salt[2] = 0;
300                 if (set_this_salt(trunc_salt) == NULL) {
301                         return NT_STATUS_NO_MEMORY;
302                 }
303         }
304 #endif
305
306         if (!get_this_crypted() || !*get_this_crypted()) {
307                 if (!lp_null_passwords()) {
308                         DEBUG(2, ("Disallowing %s with null password\n",
309                                   user));
310                         return NT_STATUS_LOGON_FAILURE;
311                 }
312                 if (!*password) {
313                         DEBUG(3,
314                               ("Allowing access to %s with null password\n",
315                                user));
316                         return NT_STATUS_OK;
317                 }
318         }
319
320 #endif /* defined(WITH_PAM) */
321
322         /* try it as it came to us */
323         nt_status = password_check(user, password, (const void *)rhost);
324         if NT_STATUS_IS_OK(nt_status) {
325                 return (nt_status);
326         } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
327                 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
328                 return (nt_status);
329         }
330
331         if (!run_cracker) {
332                 return (nt_status);
333         }
334
335         /* if the password was given to us with mixed case then we don't
336          * need to proceed as we know it hasn't been case modified by the
337          * client */
338         if (strhasupper(password) && strhaslower(password)) {
339                 return nt_status;
340         }
341
342         /* make a copy of it */
343         pass2 = talloc_strdup(talloc_tos(), password);
344         if (!pass2) {
345                 return NT_STATUS_NO_MEMORY;
346         }
347
348         /* try all lowercase if it's currently all uppercase */
349         if (strhasupper(pass2)) {
350                 if (!strlower_m(pass2)) {
351                         return NT_STATUS_INVALID_PARAMETER;
352                 }
353                 nt_status = password_check(user, pass2, (const void *)rhost);
354                 if (NT_STATUS_IS_OK(nt_status)) {
355                         return (nt_status);
356                 }
357         }
358
359         return NT_STATUS_WRONG_PASSWORD;
360 }