27e1c20cc9ddccf511369eb5c55afaf807a10c13
[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 #ifdef WITH_AFS
68
69 #include <afs/stds.h>
70 #include <afs/kautils.h>
71
72 /*******************************************************************
73 check on AFS authentication
74 ********************************************************************/
75 static bool afs_auth(char *user, char *password)
76 {
77         long password_expires = 0;
78         char *reason;
79
80         /* For versions of AFS prior to 3.3, this routine has few arguments, */
81         /* but since I can't find the old documentation... :-)               */
82         setpag();
83         if (ka_UserAuthenticateGeneral
84             (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0,       /* instance */
85              (char *)0,         /* cell */
86              password, 0,       /* lifetime, default */
87              &password_expires, /*days 'til it expires */
88              0,                 /* spare 2 */
89              &reason) == 0)
90         {
91                 return (True);
92         }
93         DEBUG(1,
94               ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
95         return (False);
96 }
97 #endif
98
99
100
101 #ifdef LINUX_BIGCRYPT
102 /****************************************************************************
103 an enhanced crypt for Linux to handle password longer than 8 characters
104 ****************************************************************************/
105 static int linux_bigcrypt(char *password, char *salt1, char *crypted)
106 {
107 #define LINUX_PASSWORD_SEG_CHARS 8
108         char salt[3];
109         int i;
110
111         StrnCpy(salt, salt1, 2);
112         crypted += 2;
113
114         for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
115                 char *p = crypt(password, salt) + 2;
116                 if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
117                         return (0);
118                 password += LINUX_PASSWORD_SEG_CHARS;
119                 crypted += strlen(p);
120         }
121
122         return (1);
123 }
124 #endif
125
126 #ifdef OSF1_ENH_SEC
127 /****************************************************************************
128 an enhanced crypt for OSF1
129 ****************************************************************************/
130 static char *osf1_bigcrypt(char *password, char *salt1)
131 {
132         static char result[AUTH_MAX_PASSWD_LENGTH] = "";
133         char *p1;
134         char *p2 = password;
135         char salt[3];
136         int i;
137         int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
138         if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
139                 parts++;
140
141         StrnCpy(salt, salt1, 2);
142         StrnCpy(result, salt1, 2);
143         result[2] = '\0';
144
145         for (i = 0; i < parts; i++) {
146                 p1 = crypt(p2, salt);
147                 strncat(result, p1 + 2,
148                         AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
149                 StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
150                 p2 += AUTH_CLEARTEXT_SEG_CHARS;
151         }
152
153         return (result);
154 }
155 #endif
156
157
158 /****************************************************************************
159 core of password checking routine
160 ****************************************************************************/
161 static NTSTATUS password_check(const char *user, const char *password, const void *private_data)
162 {
163 #ifdef WITH_PAM
164         const char *rhost = (const char *)private_data;
165         return smb_pam_passcheck(user, rhost, password);
166 #else
167
168         bool ret;
169
170 #ifdef WITH_AFS
171         if (afs_auth(user, password))
172                 return NT_STATUS_OK;
173 #endif /* WITH_AFS */
174
175
176 #ifdef OSF1_ENH_SEC
177
178         ret = (strcmp(osf1_bigcrypt(password, get_this_salt()),
179                       get_this_crypted()) == 0);
180         if (!ret) {
181                 DEBUG(2,
182                       ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
183                 ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
184         }
185         if (ret) {
186                 return NT_STATUS_OK;
187         } else {
188                 return NT_STATUS_WRONG_PASSWORD;
189         }
190
191 #endif /* OSF1_ENH_SEC */
192
193 #ifdef ULTRIX_AUTH
194         ret = (strcmp((char *)crypt16(password, get_this_salt()), get_this_crypted()) == 0);
195         if (ret) {
196                 return NT_STATUS_OK;
197         } else {
198                 return NT_STATUS_WRONG_PASSWORD;
199         }
200
201 #endif /* ULTRIX_AUTH */
202
203 #ifdef LINUX_BIGCRYPT
204         ret = (linux_bigcrypt(password, get_this_salt(), get_this_crypted()));
205         if (ret) {
206                 return NT_STATUS_OK;
207         } else {
208                 return NT_STATUS_WRONG_PASSWORD;
209         }
210 #endif /* LINUX_BIGCRYPT */
211
212 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
213
214         /*
215          * Some systems have bigcrypt in the C library but might not
216          * actually use it for the password hashes (HPUX 10.20) is
217          * a noteable example. So we try bigcrypt first, followed
218          * by crypt.
219          */
220
221         if (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0)
222                 return NT_STATUS_OK;
223         else
224                 ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
225         if (ret) {
226                 return NT_STATUS_OK;
227         } else {
228                 return NT_STATUS_WRONG_PASSWORD;
229         }
230 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
231
232 #ifdef HAVE_BIGCRYPT
233         ret = (strcmp(bigcrypt(password, get_this_salt()), get_this_crypted()) == 0);
234         if (ret) {
235                 return NT_STATUS_OK;
236         } else {
237                 return NT_STATUS_WRONG_PASSWORD;
238         }
239 #endif /* HAVE_BIGCRYPT */
240
241 #ifndef HAVE_CRYPT
242         DEBUG(1, ("Warning - no crypt available\n"));
243         return NT_STATUS_LOGON_FAILURE;
244 #else /* HAVE_CRYPT */
245         ret = (strcmp((char *)crypt(password, get_this_salt()), get_this_crypted()) == 0);
246         if (ret) {
247                 return NT_STATUS_OK;
248         } else {
249                 return NT_STATUS_WRONG_PASSWORD;
250         }
251 #endif /* HAVE_CRYPT */
252 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
253 #endif /* WITH_PAM */
254 }
255
256
257
258 /****************************************************************************
259 CHECK if a username/password is OK
260 the function pointer fn() points to a function to call when a successful
261 match is found and is used to update the encrypted password file 
262 return NT_STATUS_OK on correct match, appropriate error otherwise
263 ****************************************************************************/
264
265 NTSTATUS pass_check(const struct passwd *pass,
266                     const char *user,
267                     const char *rhost,
268                     const char *password,
269                     bool run_cracker)
270 {
271         char *pass2 = NULL;
272
273         NTSTATUS nt_status;
274
275 #ifdef DEBUG_PASSWORD
276         DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
277 #endif
278
279         if (!password)
280                 return NT_STATUS_LOGON_FAILURE;
281
282         if ((!*password) && !lp_null_passwords())
283                 return NT_STATUS_LOGON_FAILURE;
284
285 #if defined(WITH_PAM) 
286
287         /*
288          * If we're using PAM we want to short-circuit all the 
289          * checks below and dive straight into the PAM code.
290          */
291
292         DEBUG(4, ("pass_check: Checking (PAM) password for user %s\n", user));
293
294 #else /* Not using PAM */
295
296         DEBUG(4, ("pass_check: Checking password for user %s\n", user));
297
298         if (!pass) {
299                 DEBUG(3, ("Couldn't find user %s\n", user));
300                 return NT_STATUS_NO_SUCH_USER;
301         }
302
303
304         /* Copy into global for the convenience of looping code */
305         /* Also the place to keep the 'password' no matter what
306            crazy struct it started in... */
307         if (set_this_crypted(pass->pw_passwd) == NULL) {
308                 return NT_STATUS_NO_MEMORY;
309         }
310         if (set_this_salt(pass->pw_passwd) == NULL) {
311                 return NT_STATUS_NO_MEMORY;
312         }
313
314 #ifdef HAVE_GETSPNAM
315         {
316                 struct spwd *spass;
317
318                 /* many shadow systems require you to be root to get
319                    the password, in most cases this should already be
320                    the case when this function is called, except
321                    perhaps for IPC password changing requests */
322
323                 spass = getspnam(pass->pw_name);
324                 if (spass && spass->sp_pwdp) {
325                         if (set_this_crypted(spass->sp_pwdp) == NULL) {
326                                 return NT_STATUS_NO_MEMORY;
327                         }
328                         if (set_this_salt(spass->sp_pwdp) == NULL) {
329                                 return NT_STATUS_NO_MEMORY;
330                         }
331                 }
332         }
333 #elif defined(IA_UINFO)
334         {
335                 /* Need to get password with SVR4.2's ia_ functions
336                    instead of get{sp,pw}ent functions. Required by
337                    UnixWare 2.x, tested on version
338                    2.1. (tangent@cyberport.com) */
339                 uinfo_t uinfo;
340                 if (ia_openinfo(pass->pw_name, &uinfo) != -1)
341                         ia_get_logpwd(uinfo, &(pass->pw_passwd));
342         }
343 #endif
344
345 #ifdef HAVE_GETPRPWNAM
346         {
347                 struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
348                 if (pr_pw && pr_pw->ufld.fd_encrypt) {
349                         if (set_this_crypted(pr_pw->ufld.fd_encrypt) == NULL) {
350                                 return NT_STATUS_NO_MEMORY;
351                         }
352                 }
353         }
354 #endif
355
356 #ifdef HAVE_GETPWANAM
357         {
358                 struct passwd_adjunct *pwret;
359                 pwret = getpwanam(s);
360                 if (pwret && pwret->pwa_passwd) {
361                         if (set_this_crypted(pwret->pwa_passwd) == NULL) {
362                                 return NT_STATUS_NO_MEMORY;
363                         }
364                 }
365         }
366 #endif
367
368 #ifdef OSF1_ENH_SEC
369         {
370                 struct pr_passwd *mypasswd;
371                 DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
372                           user));
373                 mypasswd = getprpwnam(user);
374                 if (mypasswd) {
375                         user = mypasswd->ufld.fd_name;
376                         if (set_this_crypted(mypasswd->ufld.fd_encrypt) == NULL) {
377                                 return NT_STATUS_NO_MEMORY;
378                         }
379                 } else {
380                         DEBUG(5,
381                               ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
382                                user));
383                 }
384         }
385 #endif
386
387 #ifdef ULTRIX_AUTH
388         {
389                 AUTHORIZATION *ap = getauthuid(pass->pw_uid);
390                 if (ap) {
391                         if (set_this_crypted(ap->a_password) == NULL) {
392                                 endauthent();
393                                 return NT_STATUS_NO_MEMORY;
394                         }
395                         endauthent();
396                 }
397         }
398 #endif
399
400 #if defined(HAVE_TRUNCATED_SALT)
401         /* crypt on some platforms (HPUX in particular)
402            won't work with more than 2 salt characters. */
403         {
404                 char *trunc_salt = get_this_salt();
405                 if (!trunc_salt || strlen(trunc_salt) < 2) {
406                         return NT_STATUS_LOGON_FAILURE;
407                 }
408                 trunc_salt[2] = 0;
409                 if (set_this_salt(trunc_salt) == NULL) {
410                         return NT_STATUS_NO_MEMORY;
411                 }
412         }
413 #endif
414
415         if (!get_this_crypted() || !*get_this_crypted()) {
416                 if (!lp_null_passwords()) {
417                         DEBUG(2, ("Disallowing %s with null password\n",
418                                   user));
419                         return NT_STATUS_LOGON_FAILURE;
420                 }
421                 if (!*password) {
422                         DEBUG(3,
423                               ("Allowing access to %s with null password\n",
424                                user));
425                         return NT_STATUS_OK;
426                 }
427         }
428
429 #endif /* defined(WITH_PAM) */
430
431         /* try it as it came to us */
432         nt_status = password_check(user, password, (const void *)rhost);
433         if NT_STATUS_IS_OK(nt_status) {
434                 return (nt_status);
435         } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
436                 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
437                 return (nt_status);
438         }
439
440         if (!run_cracker) {
441                 return (nt_status);
442         }
443
444         /* if the password was given to us with mixed case then we don't
445          * need to proceed as we know it hasn't been case modified by the
446          * client */
447         if (strhasupper(password) && strhaslower(password)) {
448                 return nt_status;
449         }
450
451         /* make a copy of it */
452         pass2 = talloc_strdup(talloc_tos(), password);
453         if (!pass2) {
454                 return NT_STATUS_NO_MEMORY;
455         }
456
457         /* try all lowercase if it's currently all uppercase */
458         if (strhasupper(pass2)) {
459                 if (!strlower_m(pass2)) {
460                         return NT_STATUS_INVALID_PARAMETER;
461                 }
462                 nt_status = password_check(user, pass2, (const void *)rhost);
463                 if (NT_STATUS_IS_OK(nt_status)) {
464                         return (nt_status);
465                 }
466         }
467
468         return NT_STATUS_WRONG_PASSWORD;
469 }