f49a92a35f9af5c4af1509e20e1212ad7498e0fd
[bbaumbach/samba-autobuild/.git] / source4 / auth / auth_unix.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Password and authentication handling
4    Copyright (C) Andrew Bartlett                2001
5    Copyright (C) Jeremy Allison                 2001
6    Copyright (C) Simo Sorce                     2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "auth/auth.h"
25 #include "system/passwd.h"
26 #include "system/iconv.h"
27
28 /* TODO: look at how to best fill in parms retrieveing a struct passwd info
29  * except in case USER_INFO_DONT_CHECK_UNIX_ACCOUNT is set
30  */
31 static NTSTATUS authunix_make_server_info(TALLOC_CTX *mem_ctx,
32                                           const struct auth_usersupplied_info *user_info,
33                                           struct auth_serversupplied_info **_server_info)
34 {
35         struct auth_serversupplied_info *server_info;
36
37         server_info = talloc(mem_ctx, struct auth_serversupplied_info);
38         NT_STATUS_HAVE_NO_MEMORY(server_info);
39
40         server_info->authenticated = True;
41
42         server_info->account_name = talloc_strdup(server_info, talloc_strdup(mem_ctx, user_info->mapped.account_name));
43         NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
44
45         server_info->domain_name = talloc_strdup(server_info, talloc_strdup(mem_ctx, "unix"));
46         NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
47
48
49         /* is this correct? */
50         server_info->account_sid = NULL;
51         server_info->primary_group_sid = NULL;
52         server_info->n_domain_groups = 0;
53         server_info->domain_groups = NULL;
54         server_info->user_session_key = data_blob(NULL,0);
55         server_info->lm_session_key = data_blob(NULL,0);
56
57         server_info->full_name = talloc_strdup(server_info, "");
58         NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
59         server_info->logon_script = talloc_strdup(server_info, "");
60         NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
61         server_info->profile_path = talloc_strdup(server_info, "");
62         NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
63         server_info->home_directory = talloc_strdup(server_info, "");
64         NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
65         server_info->home_drive = talloc_strdup(server_info, "");
66         NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
67
68         server_info->last_logon = 0;
69         server_info->last_logoff = 0;
70         server_info->acct_expiry = 0;
71         server_info->last_password_change = 0;
72         server_info->allow_password_change = 0;
73         server_info->force_password_change = 0;
74         server_info->logon_count = 0;
75         server_info->bad_password_count = 0;
76         server_info->acct_flags = 0;
77
78         *_server_info = server_info;
79
80         return NT_STATUS_OK;
81 }
82
83 #ifdef HAVE_SECURITY_PAM_APPL_H
84 #include <security/pam_appl.h>
85
86 struct smb_pam_user_info {
87         const char *account_name;
88         const char *plaintext_password;
89 };
90
91 #define COPY_STRING(s) (s) ? strdup(s) : NULL
92
93 /* 
94  * Check user password
95  * Currently it uses PAM only and fails on systems without PAM
96  * Samba3 code located in pass_check.c is to ugly to be used directly it will
97  * need major rework that's why pass_check.c is still there.
98 */
99
100 static int smb_pam_conv(int num_msg, const struct pam_message **msg,
101                          struct pam_response **reply, void *appdata_ptr)
102 {
103         struct smb_pam_user_info *info = (struct smb_pam_user_info *)appdata_ptr;
104         int num;
105
106         if (num_msg <= 0) {
107                 *reply = NULL;
108                 return PAM_CONV_ERR;
109         }
110         
111         /*
112          * Apparantly HPUX has a buggy PAM that doesn't support the
113          * data pointer. Fail if this is the case. JRA.
114          */
115
116         if (info == NULL) {
117                 *reply = NULL;
118                 return PAM_CONV_ERR;
119         }
120
121         /*
122          * PAM frees memory in reply messages by itself
123          * so use malloc instead of talloc here.
124          */
125         *reply = malloc_array_p(struct pam_response, num_msg);
126         if (*reply == NULL) {
127                 return PAM_CONV_ERR;
128         }
129
130         for (num = 0; num < num_msg; num++) {
131                 switch  (msg[num]->msg_style) {
132                         case PAM_PROMPT_ECHO_ON:
133                                 (*reply)[num].resp_retcode = PAM_SUCCESS;
134                                 (*reply)[num].resp = COPY_STRING(info->account_name);
135                                 break;
136
137                         case PAM_PROMPT_ECHO_OFF:
138                                 (*reply)[num].resp_retcode = PAM_SUCCESS;
139                                 (*reply)[num].resp = COPY_STRING(info->plaintext_password);
140                                 break;
141
142                         case PAM_TEXT_INFO:
143                                 (*reply)[num].resp_retcode = PAM_SUCCESS;
144                                 (*reply)[num].resp = NULL;
145                                 DEBUG(4,("PAM Info message in conversation function: %s\n", (msg[num]->msg)));
146
147                         case PAM_ERROR_MSG:
148                                 (*reply)[num].resp_retcode = PAM_SUCCESS;
149                                 (*reply)[num].resp = NULL;
150                                 DEBUG(4,("PAM Error message in conversation function: %s\n", (msg[num]->msg)));
151                                 break;
152
153                         default:
154                                 SAFE_FREE(*reply);
155                                 *reply = NULL;
156                                 DEBUG(1,("Error: PAM subsystme sent an UNKNOWN message type to the conversation function!\n"));
157                                 return PAM_CONV_ERR;
158                 }
159         }
160
161         return PAM_SUCCESS;
162 }
163
164 /*
165  * Start PAM authentication for specified account
166  */
167
168 static NTSTATUS smb_pam_start(pam_handle_t **pamh, const char *account_name, const char *remote_host, struct pam_conv *pconv)
169 {
170         int pam_error;
171
172         if (account_name == NULL || remote_host == NULL) {
173                 return NT_STATUS_INVALID_PARAMETER;
174         }
175
176         DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", account_name));
177
178         pam_error = pam_start("samba", account_name, pconv, pamh);
179         if (pam_error != PAM_SUCCESS) {
180                 /* no vaild pamh here, can we reliably call pam_strerror ? */
181                 DEBUG(4,("smb_pam_start: pam_start failed!\n"));
182                 return NT_STATUS_UNSUCCESSFUL;
183         }
184
185 #ifdef PAM_RHOST
186         DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", remote_host));
187         pam_error = pam_set_item(*pamh, PAM_RHOST, remote_host);
188         if (pam_error != PAM_SUCCESS) {
189                 NTSTATUS nt_status;
190
191                 DEBUG(4,("smb_pam_start: setting rhost failed with error: %s\n",
192                          pam_strerror(*pamh, pam_error)));
193                 nt_status = pam_to_nt_status(pam_error);
194
195                 pam_error = pam_end(*pamh, 0);
196                 if (pam_error != PAM_SUCCESS) {
197                         /* no vaild pamh here, can we reliably call pam_strerror ? */
198                         DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
199                                  pam_error));
200                         return pam_to_nt_status(pam_error);
201                 }
202                 return nt_status;
203         }
204 #endif
205 #ifdef PAM_TTY
206         DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
207         pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
208         if (pam_error != PAM_SUCCESS) {
209                 NTSTATUS nt_status;
210
211                 DEBUG(4,("smb_pam_start: setting tty failed with error: %s\n",
212                          pam_strerror(*pamh, pam_error)));
213                 nt_status = pam_to_nt_status(pam_error);
214
215                 pam_error = pam_end(*pamh, 0);
216                 if (pam_error != PAM_SUCCESS) {
217                         /* no vaild pamh here, can we reliably call pam_strerror ? */
218                         DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
219                                  pam_error));
220                         return pam_to_nt_status(pam_error);
221                 }
222                 return nt_status;
223         }
224 #endif
225         DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", account_name));
226
227         return NT_STATUS_OK;
228 }
229
230 static NTSTATUS smb_pam_end(pam_handle_t *pamh)
231 {
232         int pam_error;
233
234         if (pamh != NULL) {
235                 pam_error = pam_end(pamh, 0);
236                 if (pam_error != PAM_SUCCESS) {
237                         /* no vaild pamh here, can we reliably call pam_strerror ? */
238                         DEBUG(4,("smb_pam_end: clean up failed, pam_end gave error %d.\n",
239                                  pam_error));
240                         return pam_to_nt_status(pam_error);
241                 }
242                 return NT_STATUS_OK;
243         }
244
245         DEBUG(2,("smb_pam_end: pamh is NULL, PAM not initialized ?\n"));
246         return NT_STATUS_UNSUCCESSFUL;
247 }
248
249 /*
250  * PAM Authentication Handler
251  */
252 static NTSTATUS smb_pam_auth(pam_handle_t *pamh, const char *user)
253 {
254         int pam_error;
255
256         /*
257          * To enable debugging set in /etc/pam.d/samba:
258          *      auth required /lib/security/pam_pwdb.so nullok shadow audit
259          */
260         
261         DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user));
262
263         pam_error = pam_authenticate(pamh, PAM_SILENT | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK);
264         switch( pam_error ){
265                 case PAM_AUTH_ERR:
266                         DEBUG(2, ("smb_pam_auth: PAM: Athentication Error for user %s\n", user));
267                         break;
268                 case PAM_CRED_INSUFFICIENT:
269                         DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
270                         break;
271                 case PAM_AUTHINFO_UNAVAIL:
272                         DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
273                         break;
274                 case PAM_USER_UNKNOWN:
275                         DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
276                         break;
277                 case PAM_MAXTRIES:
278                         DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
279                         break;
280                 case PAM_ABORT:
281                         DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
282                         break;
283                 case PAM_SUCCESS:
284                         DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
285                         break;
286                 default:
287                         DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
288                         break;
289         }
290
291         return pam_to_nt_status(pam_error);
292 }
293
294 /* 
295  * PAM Account Handler
296  */
297 static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
298 {
299         int pam_error;
300
301         DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user));
302
303         pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
304         switch( pam_error ) {
305                 case PAM_AUTHTOK_EXPIRED:
306                         DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user));
307                         break;
308                 case PAM_ACCT_EXPIRED:
309                         DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user));
310                         break;
311                 case PAM_AUTH_ERR:
312                         DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user));
313                         break;
314                 case PAM_PERM_DENIED:
315                         DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user));
316                         break;
317                 case PAM_USER_UNKNOWN:
318                         DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user));
319                         break;
320                 case PAM_SUCCESS:
321                         DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user));
322                         break;
323                 default:
324                         DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user));
325                         break;
326         }
327
328         return pam_to_nt_status(pam_error);
329 }
330
331 /*
332  * PAM Credential Setting
333  */
334
335 static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
336 {
337         int pam_error;
338
339         /*
340          * This will allow samba to aquire a kerberos token. And, when
341          * exporting an AFS cell, be able to /write/ to this cell.
342          */
343
344         DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
345
346         pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT)); 
347         switch( pam_error ) {
348                 case PAM_CRED_UNAVAIL:
349                         DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user ));
350                         break;
351                 case PAM_CRED_EXPIRED:
352                         DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user ));
353                         break;
354                 case PAM_USER_UNKNOWN:
355                         DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user ));
356                         break;
357                 case PAM_CRED_ERR:
358                         DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user ));
359                         break;
360                 case PAM_SUCCESS:
361                         DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user));
362                         break;
363                 default:
364                         DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user));
365                         break;
366         }
367
368         return pam_to_nt_status(pam_error);
369 }
370
371 static NTSTATUS check_unix_password(TALLOC_CTX *ctx, const struct auth_usersupplied_info *user_info)
372 {
373         struct smb_pam_user_info *info;
374         struct pam_conv *pamconv;
375         pam_handle_t *pamh;
376         NTSTATUS nt_status;
377
378         info = talloc(ctx, struct smb_pam_user_info);
379         if (info == NULL) {
380                 return NT_STATUS_NO_MEMORY;
381         }
382
383         info->account_name = user_info->mapped.account_name;
384         info->plaintext_password = user_info->password.plaintext;
385
386         pamconv = talloc(ctx, struct pam_conv);
387         if (pamconv == NULL) {
388                 return NT_STATUS_NO_MEMORY;
389         }
390
391         pamconv->conv = smb_pam_conv;
392         pamconv->appdata_ptr = (void *)info;
393
394         /* TODO:
395          * check for user_info->flags & USER_INFO_CASE_INSENSITIVE_USERNAME
396          * if true set up a crack name routine.
397          */
398
399         nt_status = smb_pam_start(&pamh, user_info->mapped.account_name, user_info->remote_host, pamconv);
400         if (!NT_STATUS_IS_OK(nt_status)) {
401                 smb_pam_end(pamh);
402                 return nt_status;
403         }
404
405         nt_status = smb_pam_auth(pamh, user_info->mapped.account_name);
406         if (!NT_STATUS_IS_OK(nt_status)) {
407                 smb_pam_end(pamh);
408                 return nt_status;
409         }
410
411         if ( ! (user_info->flags & USER_INFO_DONT_CHECK_UNIX_ACCOUNT)) {
412
413                 nt_status = smb_pam_account(pamh, user_info->mapped.account_name);
414                 if (!NT_STATUS_IS_OK(nt_status)) {
415                         smb_pam_end(pamh);
416                         return nt_status;
417                 }
418
419                 nt_status = smb_pam_setcred(pamh, user_info->mapped.account_name);
420                 if (!NT_STATUS_IS_OK(nt_status)) {
421                         smb_pam_end(pamh);
422                         return nt_status;
423                 }
424         }
425
426         smb_pam_end(pamh);
427         return NT_STATUS_OK;    
428 }
429
430 #else
431
432 static NTSTATUS talloc_getpwnam(TALLOC_CTX *ctx, char *username, struct passwd **pws)
433 {
434         struct passwd *ret;
435         struct passwd *from;
436
437         *pws = NULL;
438
439         ret = talloc(ctx, struct passwd);
440         NT_STATUS_HAVE_NO_MEMORY(ret);
441
442         from = getpwnam(username);
443         if (!from) {
444                 return NT_STATUS_NO_SUCH_USER;
445         }
446
447         ret->pw_name = talloc_strdup(ctx, from->pw_name);
448         NT_STATUS_HAVE_NO_MEMORY(ret->pw_name);
449
450         ret->pw_passwd = talloc_strdup(ctx, from->pw_passwd);
451         NT_STATUS_HAVE_NO_MEMORY(ret->pw_passwd);
452
453         ret->pw_uid = from->pw_uid;
454         ret->pw_gid = from->pw_gid;
455         ret->pw_gecos = talloc_strdup(ctx, from->pw_gecos);
456         NT_STATUS_HAVE_NO_MEMORY(ret->pw_gecos);
457
458         ret->pw_dir = talloc_strdup(ctx, from->pw_dir);
459         NT_STATUS_HAVE_NO_MEMORY(ret->pw_dir);
460
461         ret->pw_shell = talloc_strdup(ctx, from->pw_shell);
462         NT_STATUS_HAVE_NO_MEMORY(ret->pw_shell);
463
464         *pws = ret;
465
466         return NT_STATUS_OK;
467 }
468
469
470 /****************************************************************************
471 core of password checking routine
472 ****************************************************************************/
473 static NTSTATUS password_check(const char *username, const char *password,
474                                         const char *crypted, const char *salt)
475 {
476         BOOL ret;
477
478 #ifdef WITH_AFS
479         if (afs_auth(username, password))
480                 return NT_STATUS_OK;
481 #endif /* WITH_AFS */
482
483 #ifdef WITH_DFS
484         if (dfs_auth(username, password))
485                 return NT_STATUS_OK;
486 #endif /* WITH_DFS */
487
488 #ifdef OSF1_ENH_SEC
489         
490         ret = (strcmp(osf1_bigcrypt(password, salt), crypted) == 0);
491
492         if (!ret) {
493                 DEBUG(2,
494                       ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
495                 ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
496         }
497         if (ret) {
498                 return NT_STATUS_OK;
499         } else {
500                 return NT_STATUS_WRONG_PASSWORD;
501         }
502         
503 #endif /* OSF1_ENH_SEC */
504         
505 #ifdef ULTRIX_AUTH
506         ret = (strcmp((char *)crypt16(password, salt), crypted) == 0);
507         if (ret) {
508                 return NT_STATUS_OK;
509         } else {
510                 return NT_STATUS_WRONG_PASSWORD;
511         }
512         
513 #endif /* ULTRIX_AUTH */
514         
515 #ifdef LINUX_BIGCRYPT
516         ret = (linux_bigcrypt(password, salt, crypted));
517         if (ret) {
518                 return NT_STATUS_OK;
519         } else {
520                 return NT_STATUS_WRONG_PASSWORD;
521         }
522 #endif /* LINUX_BIGCRYPT */
523         
524 #if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
525         
526         /*
527          * Some systems have bigcrypt in the C library but might not
528          * actually use it for the password hashes (HPUX 10.20) is
529          * a noteable example. So we try bigcrypt first, followed
530          * by crypt.
531          */
532
533         if (strcmp(bigcrypt(password, salt), crypted) == 0)
534                 return NT_STATUS_OK;
535         else
536                 ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
537         if (ret) {
538                 return NT_STATUS_OK;
539         } else {
540                 return NT_STATUS_WRONG_PASSWORD;
541         }
542 #else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
543         
544 #ifdef HAVE_BIGCRYPT
545         ret = (strcmp(bigcrypt(password, salt), crypted) == 0);
546         if (ret) {
547                 return NT_STATUS_OK;
548         } else {
549                 return NT_STATUS_WRONG_PASSWORD;
550         }
551 #endif /* HAVE_BIGCRYPT */
552         
553 #ifndef HAVE_CRYPT
554         DEBUG(1, ("Warning - no crypt available\n"));
555         return NT_STATUS_LOGON_FAILURE;
556 #else /* HAVE_CRYPT */
557         ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
558         if (ret) {
559                 return NT_STATUS_OK;
560         } else {
561                 return NT_STATUS_WRONG_PASSWORD;
562         }
563 #endif /* HAVE_CRYPT */
564 #endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
565 }
566
567 static NTSTATUS check_unix_password(TALLOC_CTX *ctx, const struct auth_usersupplied_info *user_info)
568 {
569         char *username;
570         char *password;
571         char *pwcopy;
572         char *salt;
573         char *crypted;
574         struct passwd *pws;
575         NTSTATUS nt_status;
576         int level = lp_passwordlevel();
577
578         username = talloc_strdup(ctx, user_info->mapped.account_name);
579         password = talloc_strdup(ctx, user_info->password.plaintext);
580
581         nt_status = talloc_getpwnam(ctx, username, &pws);
582         if (!NT_STATUS_IS_OK(nt_status)) {
583                 return nt_status;
584         }
585
586         crypted = pws->pw_passwd;
587         salt = pws->pw_passwd;
588
589 #ifdef HAVE_GETSPNAM
590         {
591                 struct spwd *spass;
592
593                 /* many shadow systems require you to be root to get
594                    the password, in most cases this should already be
595                    the case when this function is called, except
596                    perhaps for IPC password changing requests */
597
598                 spass = getspnam(pws->pw_name);
599                 if (spass && spass->sp_pwdp) {
600                         crypted = talloc_strdup(ctx, spass->sp_pwdp);
601                         NT_STATUS_HAVE_NO_MEMORY(crypted);
602                         salt = talloc_strdup(ctx, spass->sp_pwdp);
603                         NT_STATUS_HAVE_NO_MEMORY(salt);
604                 }
605         }
606 #elif defined(IA_UINFO)
607         {
608                 char *ia_password;
609                 /* Need to get password with SVR4.2's ia_ functions
610                    instead of get{sp,pw}ent functions. Required by
611                    UnixWare 2.x, tested on version
612                    2.1. (tangent@cyberport.com) */
613                 uinfo_t uinfo;
614                 if (ia_openinfo(pws->pw_name, &uinfo) != -1) {
615                         ia_get_logpwd(uinfo, &ia_password);
616                         crypted = talloc_strdup(ctx, ia_password);
617                         NT_STATUS_HAVE_NO_MEMORY(crypted);
618                 }
619         }
620 #endif
621
622 #ifdef HAVE_GETPRPWNAM
623         {
624                 struct pr_passwd *pr_pw = getprpwnam(pws->pw_name);
625                 if (pr_pw && pr_pw->ufld.fd_encrypt) {
626                         crypted = talloc_strdup(ctx, pr_pw->ufld.fd_encrypt);
627                         NT_STATUS_HAVE_NO_MEMORY(crypted);
628                 }
629         }
630 #endif
631
632 #ifdef HAVE_GETPWANAM
633         {
634                 struct passwd_adjunct *pwret;
635                 pwret = getpwanam(s);
636                 if (pwret && pwret->pwa_passwd) {
637                         crypted = talloc_strdup(ctx, pwret->pwa_passwd);
638                         NT_STATUS_HAVE_NO_MEMORY(crypted);
639                 }
640         }
641 #endif
642
643 #ifdef OSF1_ENH_SEC
644         {
645                 struct pr_passwd *mypasswd;
646                 DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n", username));
647                 mypasswd = getprpwnam(username);
648                 if (mypasswd) {
649                         username = talloc_strdup(ctx, mypasswd->ufld.fd_name);
650                         NT_STATUS_HAVE_NO_MEMORY(username);
651                         crypted = talloc_strdup(ctx, mypasswd->ufld.fd_encrypt);
652                         NT_STATUS_HAVE_NO_MEMORY(crypted);
653                 } else {
654                         DEBUG(5,("OSF1_ENH_SEC: No entry for user %s in protected database !\n", username));
655                 }
656         }
657 #endif
658
659 #ifdef ULTRIX_AUTH
660         {
661                 AUTHORIZATION *ap = getauthuid(pws->pw_uid);
662                 if (ap) {
663                         crypted = talloc_strdup(ctx, ap->a_password);
664                         endauthent();
665                         NT_STATUS_HAVE_NO_MEMORY(crypted);
666                 }
667         }
668 #endif
669
670 #if defined(HAVE_TRUNCATED_SALT)
671         /* crypt on some platforms (HPUX in particular)
672            won't work with more than 2 salt characters. */
673         salt[2] = 0;
674 #endif
675
676         if (crypted[0] == '\0') {
677                 if (!lp_null_passwords()) {
678                         DEBUG(2, ("Disallowing %s with null password\n", username));
679                         return NT_STATUS_LOGON_FAILURE;
680                 }
681                 if (password == NULL) {
682                         DEBUG(3, ("Allowing access to %s with null password\n", username));
683                         return NT_STATUS_OK;
684                 }
685         }
686
687         /* try it as it came to us */
688         nt_status = password_check(username, password, crypted, salt);
689         if NT_STATUS_IS_OK(nt_status) {
690                 return nt_status;
691         }
692         else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
693                 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
694                 return nt_status;
695         }
696
697         if ( user_info->flags | USER_INFO_CASE_INSENSITIVE_PASSWORD) {
698                 return nt_status;
699         }
700
701         /* if the password was given to us with mixed case then we don't
702          * need to proceed as we know it hasn't been case modified by the
703          * client */
704         if (strhasupper(password) && strhaslower(password)) {
705                 return nt_status;
706         }
707
708         /* make a copy of it */
709         pwcopy = talloc_strdup(ctx, password);
710         if (!pwcopy)
711                 return NT_STATUS_NO_MEMORY;
712
713         /* try all lowercase if it's currently all uppercase */
714         if (strhasupper(pwcopy)) {
715                 strlower(pwcopy);
716                 nt_status = password_check(username, pwcopy, crypted, salt);
717                 if NT_STATUS_IS_OK(nt_status) {
718                         return nt_status;
719                 }
720         }
721
722         /* give up? */
723         if (level < 1) {
724                 return NT_STATUS_WRONG_PASSWORD;
725         }
726
727         /* last chance - all combinations of up to level chars upper! */
728         strlower(pwcopy);
729
730 #if 0
731         if (NT_STATUS_IS_OK(nt_status = string_combinations(pwcopy, password_check, level))) {
732                 return nt_status;
733         }
734 #endif   
735         return NT_STATUS_WRONG_PASSWORD;
736 }
737
738 #endif
739
740 /** Check a plaintext username/password
741  *
742  **/
743
744 static NTSTATUS authunix_check_password(struct auth_method_context *ctx,
745                                         TALLOC_CTX *mem_ctx,
746                                         const struct auth_usersupplied_info *user_info,
747                                         struct  auth_serversupplied_info **server_info)
748 {
749         TALLOC_CTX *check_ctx;
750         NTSTATUS nt_status;
751
752         if (! user_info->mapped.account_name || ! *user_info->mapped.account_name) {
753                 /* 'not for me' */
754                 return NT_STATUS_NOT_IMPLEMENTED;
755         }
756
757         if (user_info->password_state != AUTH_PASSWORD_PLAIN) {
758                 return NT_STATUS_INVALID_PARAMETER;
759         }
760
761         check_ctx = talloc_named_const(mem_ctx, 0, "check_unix_password");
762         if (check_ctx == NULL) {
763                 return NT_STATUS_NO_MEMORY;
764         }
765
766         nt_status = check_unix_password(check_ctx, user_info);
767         if ( ! NT_STATUS_IS_OK(nt_status)) {
768                 talloc_free(check_ctx);
769                 return nt_status;
770         }
771
772         nt_status = authunix_make_server_info(mem_ctx, user_info, server_info);
773         if ( ! NT_STATUS_IS_OK(nt_status)) {
774                 talloc_free(check_ctx);
775                 return nt_status;
776         }
777
778         talloc_free(check_ctx);
779         return NT_STATUS_OK;
780 }
781
782 static const struct auth_operations unix_ops = {
783         .name           = "unix",
784         .get_challenge  = auth_get_challenge_not_implemented,
785         .check_password = authunix_check_password
786 };
787
788 NTSTATUS auth_unix_init(void)
789 {
790         NTSTATUS ret;
791
792         ret = auth_register(&unix_ops);
793         if (!NT_STATUS_IS_OK(ret)) {
794                 DEBUG(0,("Failed to register unix auth backend!\n"));
795                 return ret;
796         }
797
798         return ret;
799 }