3 Copyright Andrew Tridgell <tridge@samba.org> 2000
5 largely based on pam_userdb by Christian Gafton <gafton@redhat.com>
8 #include "pam_winbind.h"
10 /* prototypes from common.c */
11 void init_request(struct winbindd_request
*req
,int rq_type
);
12 int write_sock(void *buffer
, int count
);
13 int read_reply(struct winbindd_response
*response
);
16 static void _pam_log(int err
, const char *format
, ...)
20 va_start(args
, format
);
21 openlog(MODULE_NAME
, LOG_CONS
|LOG_PID
, LOG_AUTH
);
22 vsyslog(err
, format
, args
);
29 static int _pam_parse(int argc
, const char **argv
)
31 /* step through arguments */
32 for (ctrl
= 0; argc
-- > 0; ++argv
) {
36 if (!strcmp(*argv
,"debug"))
37 ctrl
|= PAM_DEBUG_ARG
;
38 else if (!strcasecmp(*argv
, "use_authtok"))
39 ctrl
|= PAM_USE_AUTHTOK_ARG
;
40 else if (!strcasecmp(*argv
, "unknown_ok"))
41 ctrl
|= PAM_UNKNOWN_OK_ARG
;
43 _pam_log(LOG_ERR
, "pam_parse: unknown option; %s", *argv
);
50 static int winbind_request(enum winbindd_cmd req_type
,
51 struct winbindd_request
*request
,
52 struct winbindd_response
*response
)
54 /* Fill in request and send down pipe */
55 init_request(request
, req_type
);
57 if (write_sock(request
, sizeof(*request
)) == -1) {
62 if (read_reply(response
) == -1) {
66 /* Copy reply data from socket */
67 if (response
->result
!= WINBINDD_OK
) {
74 /* talk to winbindd */
75 static int winbind_auth_request(const char *user
, const char *pass
)
77 struct winbindd_request request
;
78 struct winbindd_response response
;
82 strncpy(request
.data
.auth
.user
, user
,
83 sizeof(request
.data
.auth
.user
)-1);
85 strncpy(request
.data
.auth
.pass
, pass
,
86 sizeof(request
.data
.auth
.pass
)-1);
88 return winbind_request(WINBINDD_PAM_AUTH
, &request
, &response
);
91 /* talk to winbindd */
92 static int winbind_chauthtok_request(const char *user
, const char *oldpass
,
95 struct winbindd_request request
;
96 struct winbindd_response response
;
100 if (request
.data
.chauthtok
.user
== NULL
) return -2;
102 strncpy(request
.data
.chauthtok
.user
, user
,
103 sizeof(request
.data
.chauthtok
.user
) - 1);
105 if (oldpass
!= NULL
) {
106 strncpy(request
.data
.chauthtok
.oldpass
, oldpass
,
107 sizeof(request
.data
.chauthtok
.oldpass
) - 1);
109 request
.data
.chauthtok
.oldpass
[0] = '\0';
112 if (newpass
!= NULL
) {
113 strncpy(request
.data
.chauthtok
.newpass
, newpass
,
114 sizeof(request
.data
.chauthtok
.newpass
) - 1);
116 request
.data
.chauthtok
.newpass
[0] = '\0';
119 return winbind_request(WINBINDD_PAM_CHAUTHTOK
, &request
, &response
);
123 * Looks up an user name and checks the password
128 * -1 = Password incorrect
131 static int user_lookup(const char *user
, const char *pass
)
133 return winbind_auth_request(user
, pass
);
137 * Checks if a user has an account
144 static int valid_user(const char *user
)
146 if (getpwnam(user
)) return 0;
150 /* --- authentication management functions --- */
152 /* Attempt a conversation */
154 static int converse(pam_handle_t
*pamh
, int nargs
,
155 struct pam_message
**message
,
156 struct pam_response
**response
)
159 struct pam_conv
*conv
;
161 retval
= pam_get_item(pamh
, PAM_CONV
, (const void **) &conv
) ;
162 if (retval
== PAM_SUCCESS
) {
163 retval
= conv
->conv(nargs
, (const struct pam_message
**)message
,
164 response
, conv
->appdata_ptr
);
167 return retval
; /* propagate error status */
171 static char *_pam_delete(register char *xx
)
179 * This is a conversation function to obtain the user's password
181 static int auth_conversation(pam_handle_t
*pamh
)
183 struct pam_message msg
, *pmsg
;
184 struct pam_response
*resp
;
189 msg
.msg_style
= PAM_PROMPT_ECHO_OFF
;
190 msg
.msg
= "Password: ";
192 /* so call the conversation expecting i responses */
194 retval
= converse(pamh
, 1, &pmsg
, &resp
);
198 /* interpret the response */
199 if (retval
== PAM_SUCCESS
) { /* a good conversation */
200 token
= x_strdup(resp
[0].resp
);
202 return PAM_AUTHTOK_RECOVER_ERR
;
206 /* set the auth token */
207 retval
= pam_set_item(pamh
, PAM_AUTHTOK
, token
);
208 token
= _pam_delete(token
); /* clean it up */
209 if ( (retval
!= PAM_SUCCESS
) ||
210 (retval
= pam_get_item(pamh
, PAM_AUTHTOK
, (const void **) &item
)) != PAM_SUCCESS
) {
214 _pam_drop_reply(resp
, 1);
216 retval
= (retval
== PAM_SUCCESS
)
217 ? PAM_AUTHTOK_RECOVER_ERR
:retval
;
224 int pam_sm_authenticate(pam_handle_t
*pamh
, int flags
,
225 int argc
, const char **argv
)
227 const char *username
;
228 const char *password
;
229 int retval
= PAM_AUTH_ERR
;
231 /* parse arguments */
232 ctrl
= _pam_parse(argc
, argv
);
234 /* Get the username */
235 retval
= pam_get_user(pamh
, &username
, NULL
);
236 if ((retval
!= PAM_SUCCESS
) || (!username
)) {
237 if (ctrl
& PAM_DEBUG_ARG
)
238 _pam_log(LOG_DEBUG
,"can not get the username");
239 return PAM_SERVICE_ERR
;
242 if ((ctrl
& PAM_USE_AUTHTOK_ARG
) == 0) {
243 /* Converse just to be sure we have the password */
244 retval
= auth_conversation(pamh
);
245 if (retval
!= PAM_SUCCESS
) {
246 _pam_log(LOG_ERR
, "could not obtain password for `%s'",
252 /* Get the password */
253 retval
= pam_get_item(pamh
, PAM_AUTHTOK
, (const void **) &password
);
254 if (retval
!= PAM_SUCCESS
) {
255 _pam_log(LOG_ERR
, "Could not retrive user's password");
256 return PAM_AUTHTOK_ERR
;
259 if (ctrl
& PAM_DEBUG_ARG
)
260 _pam_log(LOG_INFO
, "Verify user `%s' with password `%s'",
263 /* Now use the username to look up password */
264 retval
= user_lookup(username
, password
);
267 /* some sort of system error. The log was already printed */
268 return PAM_SERVICE_ERR
;
270 /* incorrect password */
271 _pam_log(LOG_WARNING
, "user `%s' denied access (incorrect password)", username
);
274 /* the user does not exist */
275 if (ctrl
& PAM_DEBUG_ARG
)
276 _pam_log(LOG_NOTICE
, "user `%s' not found",
278 if (ctrl
& PAM_UNKNOWN_OK_ARG
) {
281 return PAM_USER_UNKNOWN
;
283 /* Otherwise, the authentication looked good */
284 _pam_log(LOG_NOTICE
, "user '%s' granted acces", username
);
287 /* we don't know anything about this return value */
288 _pam_log(LOG_ERR
, "internal module error (retval = %d, user = `%s'",
290 return PAM_SERVICE_ERR
;
292 /* should not be reached */
297 int pam_sm_setcred(pam_handle_t
*pamh
, int flags
,
298 int argc
, const char **argv
)
304 * Account management. We want to verify that the account exists
305 * before returning PAM_SUCCESS
308 int pam_sm_acct_mgmt(pam_handle_t
*pamh
, int flags
,
309 int argc
, const char **argv
)
311 const char *username
;
312 int retval
= PAM_USER_UNKNOWN
;
314 /* parse arguments */
315 ctrl
= _pam_parse(argc
, argv
);
317 /* Get the username */
318 retval
= pam_get_user(pamh
, &username
, NULL
);
319 if ((retval
!= PAM_SUCCESS
) || (!username
)) {
320 if (ctrl
& PAM_DEBUG_ARG
)
321 _pam_log(LOG_DEBUG
,"can not get the username");
322 return PAM_SERVICE_ERR
;
325 /* Verify the username */
326 retval
= valid_user(username
);
329 /* some sort of system error. The log was already printed */
330 return PAM_SERVICE_ERR
;
332 /* the user does not exist */
333 if (ctrl
& PAM_DEBUG_ARG
)
334 _pam_log(LOG_NOTICE
, "user `%s' not found",
336 if (ctrl
& PAM_UNKNOWN_OK_ARG
)
338 return PAM_USER_UNKNOWN
;
340 /* Otherwise, the authentication looked good */
341 _pam_log(LOG_NOTICE
, "user '%s' granted acces", username
);
344 /* we don't know anything about this return value */
345 _pam_log(LOG_ERR
, "internal module error (retval = %d, user = `%s'",
347 return PAM_SERVICE_ERR
;
350 /* should not be reached */
356 int pam_sm_chauthtok(pam_handle_t
*pamh
, int flags
, int argc
,
363 /* Get name of a user */
365 retval
= pam_get_user(pamh
, &user
, "Username: ");
367 if (retval
!= PAM_SUCCESS
) {
371 /* XXX check in domain format */
373 /* Perform preliminary check and store requested password for updating
376 if (flags
& PAM_PRELIM_CHECK
) {
377 struct pam_message msg
[3], *pmsg
[3];
378 struct pam_response
*resp
;
380 /* Converse to ensure we have the current password */
382 retval
= auth_conversation(pamh
);
384 if (retval
!= PAM_SUCCESS
) {
388 /* Obtain and verify current password */
391 msg
[0].msg_style
= PAM_TEXT_INFO
;
392 msg
[0].msg
= "Changing password for user %s";
395 msg
[1].msg_style
= PAM_PROMPT_ECHO_OFF
;
396 msg
[1].msg
= "New NT password: ";
399 msg
[2].msg_style
= PAM_PROMPT_ECHO_OFF
;
400 msg
[2].msg
= "Retype new NT password: ";
404 retval
= converse(pamh
, 3, pmsg
, &resp
);
408 if (retval
== PAM_SUCCESS
) {
410 /* Check password entered correctly */
412 if (strcmp(resp
[1].resp
, resp
[2].resp
) != 0) {
413 struct pam_response
*resp2
;
415 msg
[0].msg_style
= PAM_ERROR_MSG
;
416 msg
[0].msg
= "Sorry, passwords do not match";
418 converse(pamh
, 1, pmsg
, &resp2
);
420 _pam_drop_reply(resp
, 3);
421 _pam_drop_reply(resp2
, 1);
423 return PAM_AUTHTOK_RECOVER_ERR
;
426 /* Store passwords */
428 retval
= pam_set_item(pamh
, PAM_OLDAUTHTOK
, resp
[1].resp
);
429 _pam_drop_reply(resp
, 3);
433 /* XXX What happens if root? */
434 /* XXX try first pass and use first pass args */
439 if (flags
& PAM_UPDATE_AUTHTOK
) {
441 retval
= pam_get_item(pamh
, PAM_OLDAUTHTOK
, (const void **)&newpw
);
442 if (retval
!= PAM_SUCCESS
) {
443 return PAM_AUTHTOK_ERR
;
446 retval
= pam_get_item(pamh
, PAM_AUTHTOK
, (const void **)&oldpw
);
447 if (retval
!= PAM_SUCCESS
) {
448 return PAM_AUTHTOK_ERR
;
451 fprintf(stderr
, "oldpw = %s, newpw = %s\n", oldpw
, newpw
);
453 if (retval
== PAM_SUCCESS
&&
454 winbind_chauthtok_request(user
, oldpw
, newpw
) == 0) {
458 return PAM_AUTHTOK_ERR
;
461 return PAM_SERVICE_ERR
;
466 /* static module data */
468 struct pam_module _pam_winbind_modstruct
= {
481 * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
482 * Copyright (c) Tim Potter <tpot@samba.org> 2000
484 * Redistribution and use in source and binary forms, with or without
485 * modification, are permitted provided that the following conditions
487 * 1. Redistributions of source code must retain the above copyright
488 * notice, and the entire permission notice in its entirety,
489 * including the disclaimer of warranties.
490 * 2. Redistributions in binary form must reproduce the above copyright
491 * notice, this list of conditions and the following disclaimer in the
492 * documentation and/or other materials provided with the distribution.
493 * 3. The name of the author may not be used to endorse or promote
494 * products derived from this software without specific prior
495 * written permission.
497 * ALTERNATIVELY, this product may be distributed under the terms of
498 * the GNU Public License, in which case the provisions of the GPL are
499 * required INSTEAD OF the above restrictions. (This clause is
500 * necessary due to a potential bad interaction between the GPL and
501 * the restrictions contained in a BSD-style copyright.)
503 * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
504 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
505 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
506 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
507 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
508 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
509 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
510 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
511 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
512 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
513 * OF THE POSSIBILITY OF SUCH DAMAGE.