r8933: Fix missing prototype warnings
[kai/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 /**
568  Does a string have any lowercase chars in it?
569 **/
570 static BOOL strhaslower(const char *s)
571 {
572         while (*s) {
573                 if (islower(*s)) return True;
574                 s++;
575         }
576         return False;
577 }
578
579 /**
580  Does a string have any uppercase chars in it?
581 **/
582 static BOOL strhasupper(const char *s)
583 {
584         while (*s) {
585                 if (isupper(*s)) return True;
586                 s++;
587         }
588         return False;
589 }
590
591 static NTSTATUS check_unix_password(TALLOC_CTX *ctx, const struct auth_usersupplied_info *user_info)
592 {
593         char *username;
594         char *password;
595         char *pwcopy;
596         char *salt;
597         char *crypted;
598         struct passwd *pws;
599         NTSTATUS nt_status;
600         int level = lp_passwordlevel();
601
602         username = talloc_strdup(ctx, user_info->mapped.account_name);
603         password = talloc_strdup(ctx, user_info->password.plaintext);
604
605         nt_status = talloc_getpwnam(ctx, username, &pws);
606         if (!NT_STATUS_IS_OK(nt_status)) {
607                 return nt_status;
608         }
609
610         crypted = pws->pw_passwd;
611         salt = pws->pw_passwd;
612
613 #ifdef HAVE_GETSPNAM
614         {
615                 struct spwd *spass;
616
617                 /* many shadow systems require you to be root to get
618                    the password, in most cases this should already be
619                    the case when this function is called, except
620                    perhaps for IPC password changing requests */
621
622                 spass = getspnam(pws->pw_name);
623                 if (spass && spass->sp_pwdp) {
624                         crypted = talloc_strdup(ctx, spass->sp_pwdp);
625                         NT_STATUS_HAVE_NO_MEMORY(crypted);
626                         salt = talloc_strdup(ctx, spass->sp_pwdp);
627                         NT_STATUS_HAVE_NO_MEMORY(salt);
628                 }
629         }
630 #elif defined(IA_UINFO)
631         {
632                 char *ia_password;
633                 /* Need to get password with SVR4.2's ia_ functions
634                    instead of get{sp,pw}ent functions. Required by
635                    UnixWare 2.x, tested on version
636                    2.1. (tangent@cyberport.com) */
637                 uinfo_t uinfo;
638                 if (ia_openinfo(pws->pw_name, &uinfo) != -1) {
639                         ia_get_logpwd(uinfo, &ia_password);
640                         crypted = talloc_strdup(ctx, ia_password);
641                         NT_STATUS_HAVE_NO_MEMORY(crypted);
642                 }
643         }
644 #endif
645
646 #ifdef HAVE_GETPRPWNAM
647         {
648                 struct pr_passwd *pr_pw = getprpwnam(pws->pw_name);
649                 if (pr_pw && pr_pw->ufld.fd_encrypt) {
650                         crypted = talloc_strdup(ctx, pr_pw->ufld.fd_encrypt);
651                         NT_STATUS_HAVE_NO_MEMORY(crypted);
652                 }
653         }
654 #endif
655
656 #ifdef HAVE_GETPWANAM
657         {
658                 struct passwd_adjunct *pwret;
659                 pwret = getpwanam(s);
660                 if (pwret && pwret->pwa_passwd) {
661                         crypted = talloc_strdup(ctx, pwret->pwa_passwd);
662                         NT_STATUS_HAVE_NO_MEMORY(crypted);
663                 }
664         }
665 #endif
666
667 #ifdef OSF1_ENH_SEC
668         {
669                 struct pr_passwd *mypasswd;
670                 DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n", username));
671                 mypasswd = getprpwnam(username);
672                 if (mypasswd) {
673                         username = talloc_strdup(ctx, mypasswd->ufld.fd_name);
674                         NT_STATUS_HAVE_NO_MEMORY(username);
675                         crypted = talloc_strdup(ctx, mypasswd->ufld.fd_encrypt);
676                         NT_STATUS_HAVE_NO_MEMORY(crypted);
677                 } else {
678                         DEBUG(5,("OSF1_ENH_SEC: No entry for user %s in protected database !\n", username));
679                 }
680         }
681 #endif
682
683 #ifdef ULTRIX_AUTH
684         {
685                 AUTHORIZATION *ap = getauthuid(pws->pw_uid);
686                 if (ap) {
687                         crypted = talloc_strdup(ctx, ap->a_password);
688                         endauthent();
689                         NT_STATUS_HAVE_NO_MEMORY(crypted);
690                 }
691         }
692 #endif
693
694 #if defined(HAVE_TRUNCATED_SALT)
695         /* crypt on some platforms (HPUX in particular)
696            won't work with more than 2 salt characters. */
697         salt[2] = 0;
698 #endif
699
700         if (crypted[0] == '\0') {
701                 if (!lp_null_passwords()) {
702                         DEBUG(2, ("Disallowing %s with null password\n", username));
703                         return NT_STATUS_LOGON_FAILURE;
704                 }
705                 if (password == NULL) {
706                         DEBUG(3, ("Allowing access to %s with null password\n", username));
707                         return NT_STATUS_OK;
708                 }
709         }
710
711         /* try it as it came to us */
712         nt_status = password_check(username, password, crypted, salt);
713         if NT_STATUS_IS_OK(nt_status) {
714                 return nt_status;
715         }
716         else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
717                 /* No point continuing if its not the password thats to blame (ie PAM disabled). */
718                 return nt_status;
719         }
720
721         if ( user_info->flags | USER_INFO_CASE_INSENSITIVE_PASSWORD) {
722                 return nt_status;
723         }
724
725         /* if the password was given to us with mixed case then we don't
726          * need to proceed as we know it hasn't been case modified by the
727          * client */
728         if (strhasupper(password) && strhaslower(password)) {
729                 return nt_status;
730         }
731
732         /* make a copy of it */
733         pwcopy = talloc_strdup(ctx, password);
734         if (!pwcopy)
735                 return NT_STATUS_NO_MEMORY;
736
737         /* try all lowercase if it's currently all uppercase */
738         if (strhasupper(pwcopy)) {
739                 strlower(pwcopy);
740                 nt_status = password_check(username, pwcopy, crypted, salt);
741                 if NT_STATUS_IS_OK(nt_status) {
742                         return nt_status;
743                 }
744         }
745
746         /* give up? */
747         if (level < 1) {
748                 return NT_STATUS_WRONG_PASSWORD;
749         }
750
751         /* last chance - all combinations of up to level chars upper! */
752         strlower(pwcopy);
753
754 #if 0
755         if (NT_STATUS_IS_OK(nt_status = string_combinations(pwcopy, password_check, level))) {
756                 return nt_status;
757         }
758 #endif   
759         return NT_STATUS_WRONG_PASSWORD;
760 }
761
762 #endif
763
764 /** Check a plaintext username/password
765  *
766  **/
767
768 static NTSTATUS authunix_check_password(struct auth_method_context *ctx,
769                                         TALLOC_CTX *mem_ctx,
770                                         const struct auth_usersupplied_info *user_info,
771                                         struct  auth_serversupplied_info **server_info)
772 {
773         TALLOC_CTX *check_ctx;
774         NTSTATUS nt_status;
775
776         if (! user_info->mapped.account_name || ! *user_info->mapped.account_name) {
777                 /* 'not for me' */
778                 return NT_STATUS_NOT_IMPLEMENTED;
779         }
780
781         if (user_info->password_state != AUTH_PASSWORD_PLAIN) {
782                 return NT_STATUS_INVALID_PARAMETER;
783         }
784
785         check_ctx = talloc_named_const(mem_ctx, 0, "check_unix_password");
786         if (check_ctx == NULL) {
787                 return NT_STATUS_NO_MEMORY;
788         }
789
790         nt_status = check_unix_password(check_ctx, user_info);
791         if ( ! NT_STATUS_IS_OK(nt_status)) {
792                 talloc_free(check_ctx);
793                 return nt_status;
794         }
795
796         nt_status = authunix_make_server_info(mem_ctx, user_info, server_info);
797         if ( ! NT_STATUS_IS_OK(nt_status)) {
798                 talloc_free(check_ctx);
799                 return nt_status;
800         }
801
802         talloc_free(check_ctx);
803         return NT_STATUS_OK;
804 }
805
806 static const struct auth_operations unix_ops = {
807         .name           = "unix",
808         .get_challenge  = auth_get_challenge_not_implemented,
809         .check_password = authunix_check_password
810 };
811
812 NTSTATUS auth_unix_init(void)
813 {
814         NTSTATUS ret;
815
816         ret = auth_register(&unix_ops);
817         if (!NT_STATUS_IS_OK(ret)) {
818                 DEBUG(0,("Failed to register unix auth backend!\n"));
819                 return ret;
820         }
821
822         return ret;
823 }