3 Copyright Andrew Tridgell <tridge@samba.org> 2000
4 Copyright Tim Potter <tpot@samba.org> 2000
5 Copyright Andrew Bartlett <abartlet@samba.org> 2002
7 largely based on pam_userdb by Christian Gafton <gafton@redhat.com>
8 also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
9 (see copyright below for full details)
12 #include "pam_winbind.h"
14 /* prototypes from common.c */
15 void init_request(struct winbindd_request
*req
,int rq_type
);
16 int write_sock(void *buffer
, int count
);
17 int read_reply(struct winbindd_response
*response
);
21 #define MAX_PASSWD_TRIES 3
24 static void _pam_log(int err
, const char *format
, ...)
28 va_start(args
, format
);
29 openlog(MODULE_NAME
, LOG_CONS
|LOG_PID
, LOG_AUTH
);
30 vsyslog(err
, format
, args
);
35 static int _pam_parse(int argc
, const char **argv
)
38 /* step through arguments */
39 for (ctrl
= 0; argc
-- > 0; ++argv
) {
43 if (!strcmp(*argv
,"debug"))
44 ctrl
|= WINBIND_DEBUG_ARG
;
45 else if (!strcasecmp(*argv
, "use_authtok"))
46 ctrl
|= WINBIND_USE_AUTHTOK_ARG
;
47 else if (!strcasecmp(*argv
, "use_first_pass"))
48 ctrl
|= WINBIND_TRY_FIRST_PASS_ARG
;
49 else if (!strcasecmp(*argv
, "try_first_pass"))
50 ctrl
|= WINBIND_USE_FIRST_PASS_ARG
;
51 else if (!strcasecmp(*argv
, "unknown_ok"))
52 ctrl
|= WINBIND_UNKNOWN_OK_ARG
;
54 _pam_log(LOG_ERR
, "pam_parse: unknown option; %s", *argv
);
61 /* --- authentication management functions --- */
63 /* Attempt a conversation */
65 static int converse(pam_handle_t
*pamh
, int nargs
,
66 struct pam_message
**message
,
67 struct pam_response
**response
)
70 struct pam_conv
*conv
;
72 retval
= pam_get_item(pamh
, PAM_CONV
, (const void **) &conv
) ;
73 if (retval
== PAM_SUCCESS
) {
74 retval
= conv
->conv(nargs
, (const struct pam_message
**)message
,
75 response
, conv
->appdata_ptr
);
78 return retval
; /* propagate error status */
82 int _make_remark(pam_handle_t
* pamh
, int type
, const char *text
)
84 int retval
= PAM_SUCCESS
;
86 struct pam_message
*pmsg
[1], msg
[1];
87 struct pam_response
*resp
;
91 msg
[0].msg_style
= type
;
94 retval
= converse(pamh
, 1, pmsg
, &resp
);
97 _pam_drop_reply(resp
, 1);
102 static int winbind_request(enum winbindd_cmd req_type
,
103 struct winbindd_request
*request
,
104 struct winbindd_response
*response
)
106 /* Fill in request and send down pipe */
107 init_request(request
, req_type
);
109 if (write_sock(request
, sizeof(*request
)) == -1) {
110 _pam_log(LOG_ERR
, "write to socket failed!");
111 return PAM_SERVICE_ERR
;
115 if (read_reply(response
) == -1) {
116 _pam_log(LOG_ERR
, "read from socket failed!");
117 return PAM_SERVICE_ERR
;
120 /* Copy reply data from socket */
121 if (response
->result
!= WINBINDD_OK
) {
122 if (response
->data
.auth
.pam_error
!= PAM_SUCCESS
) {
123 _pam_log(LOG_ERR
, "request failed, PAM error was %d, NT error was %s",
124 response
->data
.auth
.pam_error
,
125 response
->data
.auth
.nt_status_string
);
126 return response
->data
.auth
.pam_error
;
128 _pam_log(LOG_ERR
, "request failed, but PAM error 0!");
129 return PAM_SERVICE_ERR
;
136 /* talk to winbindd */
137 static int winbind_auth_request(const char *user
, const char *pass
, int ctrl
)
139 struct winbindd_request request
;
140 struct winbindd_response response
;
143 ZERO_STRUCT(request
);
145 strncpy(request
.data
.auth
.user
, user
,
146 sizeof(request
.data
.auth
.user
)-1);
148 strncpy(request
.data
.auth
.pass
, pass
,
149 sizeof(request
.data
.auth
.pass
)-1);
151 retval
= winbind_request(WINBINDD_PAM_AUTH
, &request
, &response
);
155 /* incorrect password */
156 _pam_log(LOG_WARNING
, "user `%s' denied access (incorrect password)", user
);
158 case PAM_USER_UNKNOWN
:
159 /* the user does not exist */
160 if (ctrl
& WINBIND_DEBUG_ARG
)
161 _pam_log(LOG_NOTICE
, "user `%s' not found",
163 if (ctrl
& WINBIND_UNKNOWN_OK_ARG
) {
168 /* Otherwise, the authentication looked good */
169 _pam_log(LOG_NOTICE
, "user '%s' granted acces", user
);
172 /* we don't know anything about this return value */
173 _pam_log(LOG_ERR
, "internal module error (retval = %d, user = `%s'",
177 /* should not be reached */
180 /* talk to winbindd */
181 static int winbind_chauthtok_request(const char *user
, const char *oldpass
,
184 struct winbindd_request request
;
185 struct winbindd_response response
;
187 ZERO_STRUCT(request
);
189 if (request
.data
.chauthtok
.user
== NULL
) return -2;
191 strncpy(request
.data
.chauthtok
.user
, user
,
192 sizeof(request
.data
.chauthtok
.user
) - 1);
194 if (oldpass
!= NULL
) {
195 strncpy(request
.data
.chauthtok
.oldpass
, oldpass
,
196 sizeof(request
.data
.chauthtok
.oldpass
) - 1);
198 request
.data
.chauthtok
.oldpass
[0] = '\0';
201 if (newpass
!= NULL
) {
202 strncpy(request
.data
.chauthtok
.newpass
, newpass
,
203 sizeof(request
.data
.chauthtok
.newpass
) - 1);
205 request
.data
.chauthtok
.newpass
[0] = '\0';
208 return winbind_request(WINBINDD_PAM_CHAUTHTOK
, &request
, &response
);
212 * Checks if a user has an account
219 static int valid_user(const char *user
)
221 if (getpwnam(user
)) return 0;
225 static char *_pam_delete(register char *xx
)
233 * obtain a password from the user
236 int _winbind_read_password(pam_handle_t
* pamh
249 * make sure nothing inappropriate gets returned
252 *pass
= token
= NULL
;
255 * which authentication token are we getting?
258 authtok_flag
= on(WINBIND__OLD_PASSWORD
, ctrl
) ? PAM_OLDAUTHTOK
: PAM_AUTHTOK
;
261 * should we obtain the password from a PAM item ?
264 if (on(WINBIND_TRY_FIRST_PASS_ARG
, ctrl
) || on(WINBIND_USE_FIRST_PASS_ARG
, ctrl
)) {
265 retval
= pam_get_item(pamh
, authtok_flag
, (const void **) &item
);
266 if (retval
!= PAM_SUCCESS
) {
269 "pam_get_item returned error to unix-read-password"
272 } else if (item
!= NULL
) { /* we have a password! */
276 } else if (on(WINBIND_USE_FIRST_PASS_ARG
, ctrl
)) {
277 return PAM_AUTHTOK_RECOVER_ERR
; /* didn't work */
278 } else if (on(WINBIND_USE_AUTHTOK_ARG
, ctrl
)
279 && off(WINBIND__OLD_PASSWORD
, ctrl
)) {
280 return PAM_AUTHTOK_RECOVER_ERR
;
284 * getting here implies we will have to get the password from the
289 struct pam_message msg
[3], *pmsg
[3];
290 struct pam_response
*resp
;
293 /* prepare to converse */
295 if (comment
!= NULL
) {
297 msg
[0].msg_style
= PAM_TEXT_INFO
;
298 msg
[0].msg
= comment
;
305 msg
[i
].msg_style
= PAM_PROMPT_ECHO_OFF
;
306 msg
[i
++].msg
= prompt1
;
309 if (prompt2
!= NULL
) {
311 msg
[i
].msg_style
= PAM_PROMPT_ECHO_OFF
;
312 msg
[i
++].msg
= prompt2
;
315 /* so call the conversation expecting i responses */
317 retval
= converse(pamh
, i
, pmsg
, &resp
);
321 /* interpret the response */
323 if (retval
== PAM_SUCCESS
) { /* a good conversation */
325 token
= x_strdup(resp
[i
- replies
].resp
);
329 /* verify that password entered correctly */
330 if (!resp
[i
- 1].resp
331 || strcmp(token
, resp
[i
- 1].resp
)) {
332 _pam_delete(token
); /* mistyped */
333 retval
= PAM_AUTHTOK_RECOVER_ERR
;
334 _make_remark(pamh
,PAM_ERROR_MSG
, MISTYPED_PASS
);
339 ,"could not recover authentication token");
344 * tidy up the conversation (resp_retcode) is ignored
345 * -- what is it for anyway? AGM
348 _pam_drop_reply(resp
, i
);
351 retval
= (retval
== PAM_SUCCESS
)
352 ? PAM_AUTHTOK_RECOVER_ERR
: retval
;
356 if (retval
!= PAM_SUCCESS
) {
357 if (on(WINBIND_DEBUG_ARG
, ctrl
))
359 "unable to obtain a password");
362 /* 'token' is the entered password */
364 /* we store this password as an item */
366 retval
= pam_set_item(pamh
, authtok_flag
, token
);
367 _pam_delete(token
); /* clean it up */
368 if (retval
!= PAM_SUCCESS
369 || (retval
= pam_get_item(pamh
, authtok_flag
370 ,(const void **) &item
))
373 _pam_log(LOG_CRIT
, "error manipulating password");
379 item
= NULL
; /* break link to password */
385 int pam_sm_authenticate(pam_handle_t
*pamh
, int flags
,
386 int argc
, const char **argv
)
388 const char *username
;
389 const char *password
;
390 int retval
= PAM_AUTH_ERR
;
392 /* parse arguments */
393 int ctrl
= _pam_parse(argc
, argv
);
395 /* Get the username */
396 retval
= pam_get_user(pamh
, &username
, NULL
);
397 if ((retval
!= PAM_SUCCESS
) || (!username
)) {
398 if (ctrl
& WINBIND_DEBUG_ARG
)
399 _pam_log(LOG_DEBUG
,"can not get the username");
400 return PAM_SERVICE_ERR
;
403 retval
= _winbind_read_password(pamh
, ctrl
, NULL
,
407 if (retval
!= PAM_SUCCESS
) {
408 _pam_log(LOG_ERR
, "Could not retrive user's password");
409 return PAM_AUTHTOK_ERR
;
412 if (ctrl
& WINBIND_DEBUG_ARG
) {
414 /* Let's not give too much away in the log file */
416 #ifdef DEBUG_PASSWORD
417 _pam_log(LOG_INFO
, "Verify user `%s' with password `%s'",
420 _pam_log(LOG_INFO
, "Verify user `%s'", username
);
424 /* Now use the username to look up password */
425 return winbind_auth_request(username
, password
, ctrl
);
429 int pam_sm_setcred(pam_handle_t
*pamh
, int flags
,
430 int argc
, const char **argv
)
436 * Account management. We want to verify that the account exists
437 * before returning PAM_SUCCESS
440 int pam_sm_acct_mgmt(pam_handle_t
*pamh
, int flags
,
441 int argc
, const char **argv
)
443 const char *username
;
444 int retval
= PAM_USER_UNKNOWN
;
446 /* parse arguments */
447 int ctrl
= _pam_parse(argc
, argv
);
449 /* Get the username */
450 retval
= pam_get_user(pamh
, &username
, NULL
);
451 if ((retval
!= PAM_SUCCESS
) || (!username
)) {
452 if (ctrl
& WINBIND_DEBUG_ARG
)
453 _pam_log(LOG_DEBUG
,"can not get the username");
454 return PAM_SERVICE_ERR
;
457 /* Verify the username */
458 retval
= valid_user(username
);
461 /* some sort of system error. The log was already printed */
462 return PAM_SERVICE_ERR
;
464 /* the user does not exist */
465 if (ctrl
& WINBIND_DEBUG_ARG
)
466 _pam_log(LOG_NOTICE
, "user `%s' not found",
468 if (ctrl
& WINBIND_UNKNOWN_OK_ARG
)
470 return PAM_USER_UNKNOWN
;
472 /* Otherwise, the authentication looked good */
473 _pam_log(LOG_NOTICE
, "user '%s' granted acces", username
);
476 /* we don't know anything about this return value */
477 _pam_log(LOG_ERR
, "internal module error (retval = %d, user = `%s'",
479 return PAM_SERVICE_ERR
;
482 /* should not be reached */
486 PAM_EXTERN
int pam_sm_open_session(pam_handle_t
*pamh
, int flags
,
487 int argc
, const char **argv
)
489 /* parse arguments */
490 int ctrl
= _pam_parse(argc
, argv
);
491 if (ctrl
& WINBIND_DEBUG_ARG
)
492 _pam_log(LOG_DEBUG
,"libpam_winbind:pam_sm_open_session handler");
496 PAM_EXTERN
int pam_sm_close_session(pam_handle_t
*pamh
, int flags
,
497 int argc
, const char **argv
)
499 /* parse arguments */
500 int ctrl
= _pam_parse(argc
, argv
);
501 if (ctrl
& WINBIND_DEBUG_ARG
)
502 _pam_log(LOG_DEBUG
,"libpam_winbind:pam_sm_close_session handler");
506 PAM_EXTERN
int pam_sm_chauthtok(pam_handle_t
* pamh
, int flags
,
507 int argc
, const char **argv
)
511 unsigned int ctrl
= _pam_parse(argc
, argv
);
513 /* <DO NOT free() THESE> */
515 char *pass_old
, *pass_new
;
516 /* </DO NOT free() THESE> */
523 * First get the name of a user
525 retval
= pam_get_user(pamh
, &user
, "Username: ");
526 if (retval
== PAM_SUCCESS
) {
528 _pam_log(LOG_ERR
, "username was NULL!");
529 return PAM_USER_UNKNOWN
;
531 if (retval
== PAM_SUCCESS
&& on(WINBIND_DEBUG_ARG
, ctrl
))
532 _pam_log(LOG_DEBUG
, "username [%s] obtained",
535 if (on(WINBIND_DEBUG_ARG
, ctrl
))
537 "password - could not identify user");
542 * obtain and verify the current password (OLDAUTHTOK) for
546 if (flags
& PAM_PRELIM_CHECK
) {
548 /* instruct user what is happening */
549 #define greeting "Changing password for "
550 Announce
= (char *) malloc(sizeof(greeting
) + strlen(user
));
551 if (Announce
== NULL
) {
553 "password - out of memory");
556 (void) strcpy(Announce
, greeting
);
557 (void) strcpy(Announce
+ sizeof(greeting
) - 1, user
);
560 lctrl
= ctrl
| WINBIND__OLD_PASSWORD
;
561 retval
= _winbind_read_password(pamh
, lctrl
563 ,"(current) NT password: "
565 ,(const char **) &pass_old
);
568 if (retval
!= PAM_SUCCESS
) {
570 ,"password - (old) token not obtained");
573 /* verify that this is the password for this user */
575 retval
= winbind_auth_request(user
, pass_old
, ctrl
);
577 if (retval
!= PAM_ACCT_EXPIRED
578 && retval
!= PAM_NEW_AUTHTOK_REQD
579 && retval
!= PAM_SUCCESS
) {
584 retval
= pam_set_item(pamh
, PAM_OLDAUTHTOK
, (const void *) pass_old
);
586 if (retval
!= PAM_SUCCESS
) {
588 "failed to set PAM_OLDAUTHTOK");
590 } else if (flags
& PAM_UPDATE_AUTHTOK
) {
593 * obtain the proposed password
597 * get the old token back.
600 retval
= pam_get_item(pamh
, PAM_OLDAUTHTOK
601 ,(const void **) &pass_old
);
603 if (retval
!= PAM_SUCCESS
) {
604 _pam_log(LOG_NOTICE
, "user not authenticated");
610 if (on(WINBIND_USE_AUTHTOK_ARG
, lctrl
)) {
611 ctrl
= WINBIND_USE_FIRST_PASS_ARG
| lctrl
;
614 retval
= PAM_AUTHTOK_ERR
;
615 while ((retval
!= PAM_SUCCESS
) && (retry
++ < MAX_PASSWD_TRIES
)) {
617 * use_authtok is to force the use of a previously entered
618 * password -- needed for pluggable password strength checking
621 retval
= _winbind_read_password(pamh
, lctrl
623 ,"Enter new NT password: "
624 ,"Retype new NT password: "
625 ,(const char **) &pass_new
);
627 if (retval
!= PAM_SUCCESS
) {
628 if (on(WINBIND_DEBUG_ARG
, ctrl
)) {
630 ,"password - new password not obtained");
632 pass_old
= NULL
;/* tidy up */
637 * At this point we know who the user is and what they
638 * propose as their new password. Verify that the new
639 * password is acceptable.
642 if (pass_new
[0] == '\0') {/* "\0" password = NULL */
648 * By reaching here we have approved the passwords and must now
649 * rebuild the password database file.
652 retval
= winbind_chauthtok_request(user
, pass_old
, pass_new
);
653 _pam_overwrite(pass_new
);
654 _pam_overwrite(pass_old
);
655 pass_old
= pass_new
= NULL
;
657 retval
= PAM_SERVICE_ERR
;
665 /* static module data */
667 struct pam_module _pam_winbind_modstruct
= {
680 * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
681 * Copyright (c) Tim Potter <tpot@samba.org> 2000
682 * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
683 * Copyright (c) Jan Rêkorajski 1999.
684 * Copyright (c) Andrew G. Morgan 1996-8.
685 * Copyright (c) Alex O. Yuriev, 1996.
686 * Copyright (c) Cristian Gafton 1996.
687 * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
689 * Redistribution and use in source and binary forms, with or without
690 * modification, are permitted provided that the following conditions
692 * 1. Redistributions of source code must retain the above copyright
693 * notice, and the entire permission notice in its entirety,
694 * including the disclaimer of warranties.
695 * 2. Redistributions in binary form must reproduce the above copyright
696 * notice, this list of conditions and the following disclaimer in the
697 * documentation and/or other materials provided with the distribution.
698 * 3. The name of the author may not be used to endorse or promote
699 * products derived from this software without specific prior
700 * written permission.
702 * ALTERNATIVELY, this product may be distributed under the terms of
703 * the GNU Public License, in which case the provisions of the GPL are
704 * required INSTEAD OF the above restrictions. (This clause is
705 * necessary due to a potential bad interaction between the GPL and
706 * the restrictions contained in a BSD-style copyright.)
708 * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
709 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
710 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
711 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
712 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
713 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
714 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
715 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
716 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
717 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
718 * OF THE POSSIBILITY OF SUCH DAMAGE.