iAdditional files for winbind merge.
[samba.git] / source3 / nsswitch / pam_winbind.c
1 /* pam_winbind module
2
3    Copyright Andrew Tridgell <tridge@samba.org> 2000
4
5    largely based on pam_userdb by Christian Gafton <gafton@redhat.com> 
6 */
7
8 #include "pam_winbind.h"
9
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);
14
15 /* some syslogging */
16 static void _pam_log(int err, const char *format, ...)
17 {
18         va_list args;
19
20         va_start(args, format);
21         openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
22         vsyslog(err, format, args);
23         va_end(args);
24         closelog();
25 }
26
27 static int ctrl  = 0;
28
29 static int _pam_parse(int argc, const char **argv)
30 {
31      /* step through arguments */
32      for (ctrl = 0; argc-- > 0; ++argv) {
33
34           /* generic options */
35
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;
42           else {
43                _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
44           }
45      }
46
47      return ctrl;
48 }
49
50 static int winbind_request(enum winbindd_cmd req_type,
51                            struct winbindd_request *request,
52                            struct winbindd_response *response)
53 {
54         /* Fill in request and send down pipe */
55         init_request(request, req_type);
56         
57         if (write_sock(request, sizeof(*request)) == -1) {
58                 return -2;
59         }
60         
61         /* Wait for reply */
62         if (read_reply(response) == -1) {
63                 return -2;
64         }
65
66         /* Copy reply data from socket */
67         if (response->result != WINBINDD_OK) {
68                 return 1;
69         }
70         
71         return 0;
72 }
73
74 /* talk to winbindd */
75 static int winbind_auth_request(const char *user, const char *pass)
76 {
77         struct winbindd_request request;
78         struct winbindd_response response;
79
80         ZERO_STRUCT(request);
81
82         strncpy(request.data.auth.user, user, 
83                 sizeof(request.data.auth.user)-1);
84
85         strncpy(request.data.auth.pass, pass, 
86                 sizeof(request.data.auth.pass)-1);
87         
88         return winbind_request(WINBINDD_PAM_AUTH, &request, &response);
89 }
90
91 /* talk to winbindd */
92 static int winbind_chauthtok_request(const char *user, const char *oldpass,
93                                      const char *newpass)
94 {
95         struct winbindd_request request;
96         struct winbindd_response response;
97
98         ZERO_STRUCT(request);
99
100         if (request.data.chauthtok.user == NULL) return -2;
101
102         strncpy(request.data.chauthtok.user, user, 
103                 sizeof(request.data.chauthtok.user) - 1);
104
105         if (oldpass != NULL) {
106             strncpy(request.data.chauthtok.oldpass, oldpass, 
107                     sizeof(request.data.chauthtok.oldpass) - 1);
108         } else {
109             request.data.chauthtok.oldpass[0] = '\0';
110         }
111         
112         if (newpass != NULL) {
113             strncpy(request.data.chauthtok.newpass, newpass, 
114                     sizeof(request.data.chauthtok.newpass) - 1);
115         } else {
116             request.data.chauthtok.newpass[0] = '\0';
117         }
118         
119         return winbind_request(WINBINDD_PAM_CHAUTHTOK, &request, &response);
120 }
121
122 /*
123  * Looks up an user name and checks the password
124  *
125  * return values:
126  *       1  = User not found
127  *       0  = OK
128  *      -1  = Password incorrect
129  *      -2  = System error
130  */
131 static int user_lookup(const char *user, const char *pass)
132 {
133         return winbind_auth_request(user, pass);
134 }
135
136 /*
137  * Checks if a user has an account
138  *
139  * return values:
140  *       1  = User not found
141  *       0  = OK
142  *      -1  = System error
143  */
144 static int valid_user(const char *user)
145 {
146         if (getpwnam(user)) return 0;
147         return 1;
148 }
149
150 /* --- authentication management functions --- */
151
152 /* Attempt a conversation */
153
154 static int converse(pam_handle_t *pamh, int nargs,
155                     struct pam_message **message,
156                     struct pam_response **response)
157 {
158     int retval;
159     struct pam_conv *conv;
160
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);
165     }
166         
167     return retval; /* propagate error status */
168 }
169
170
171 static char *_pam_delete(register char *xx)
172 {
173     _pam_overwrite(xx);
174     _pam_drop(xx);
175     return NULL;
176 }
177
178 /*
179  * This is a conversation function to obtain the user's password
180  */
181 static int auth_conversation(pam_handle_t *pamh)
182 {
183     struct pam_message msg, *pmsg;
184     struct pam_response *resp;
185     int retval;
186     char * token = NULL;
187     
188     pmsg = &msg;
189     msg.msg_style = PAM_PROMPT_ECHO_OFF;
190     msg.msg = "Password: ";
191
192     /* so call the conversation expecting i responses */
193     resp = NULL;
194     retval = converse(pamh, 1, &pmsg, &resp);
195
196     if (resp != NULL) {
197         char * const item;
198         /* interpret the response */
199         if (retval == PAM_SUCCESS) {     /* a good conversation */
200             token = x_strdup(resp[0].resp);
201             if (token == NULL) {
202                 return PAM_AUTHTOK_RECOVER_ERR;
203             }
204         }
205
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 ) {
211             return retval;
212         }
213         
214         _pam_drop_reply(resp, 1);
215     } else {
216         retval = (retval == PAM_SUCCESS)
217             ? PAM_AUTHTOK_RECOVER_ERR:retval ;
218     }
219
220     return retval;
221 }
222
223 PAM_EXTERN
224 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
225                         int argc, const char **argv)
226 {
227      const char *username;
228      const char *password;
229      int retval = PAM_AUTH_ERR;
230     
231      /* parse arguments */
232      ctrl = _pam_parse(argc, argv);
233
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;
240      }
241      
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'",
247                       username);
248              return PAM_CONV_ERR;
249          }
250      }
251      
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;
257      }
258      
259      if (ctrl & PAM_DEBUG_ARG)
260          _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
261                   username, password);
262      
263      /* Now use the username to look up password */
264      retval = user_lookup(username, password);
265      switch (retval) {
266          case -2:
267              /* some sort of system error. The log was already printed */
268              return PAM_SERVICE_ERR;    
269          case -1:
270              /* incorrect password */
271              _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password)", username);
272              return PAM_AUTH_ERR;
273          case 1:
274                  /* the user does not exist */
275              if (ctrl & PAM_DEBUG_ARG)
276                  _pam_log(LOG_NOTICE, "user `%s' not found",
277                           username);
278              if (ctrl & PAM_UNKNOWN_OK_ARG) {
279                  return PAM_IGNORE;
280              }   
281              return PAM_USER_UNKNOWN;
282          case 0:
283              /* Otherwise, the authentication looked good */
284              _pam_log(LOG_NOTICE, "user '%s' granted acces", username);
285              return PAM_SUCCESS;
286          default:
287              /* we don't know anything about this return value */
288              _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
289                       retval, username);
290              return PAM_SERVICE_ERR;
291      }
292      /* should not be reached */
293      return PAM_IGNORE;
294 }
295
296 PAM_EXTERN
297 int pam_sm_setcred(pam_handle_t *pamh, int flags,
298                    int argc, const char **argv)
299 {
300     return PAM_SUCCESS;
301 }
302
303 /*
304  * Account management. We want to verify that the account exists 
305  * before returning PAM_SUCCESS
306  */
307 PAM_EXTERN
308 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
309                    int argc, const char **argv)
310 {
311     const char *username;
312     int retval = PAM_USER_UNKNOWN;
313
314     /* parse arguments */
315     ctrl = _pam_parse(argc, argv);
316
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;
323     }
324
325     /* Verify the username */
326     retval = valid_user(username);
327     switch (retval) {
328         case -1:
329             /* some sort of system error. The log was already printed */
330             return PAM_SERVICE_ERR;
331         case 1:
332             /* the user does not exist */
333             if (ctrl & PAM_DEBUG_ARG)
334                 _pam_log(LOG_NOTICE, "user `%s' not found",
335                          username);
336             if (ctrl & PAM_UNKNOWN_OK_ARG)
337                 return PAM_IGNORE;
338             return PAM_USER_UNKNOWN;
339         case 0:
340             /* Otherwise, the authentication looked good */
341             _pam_log(LOG_NOTICE, "user '%s' granted acces", username);
342             return PAM_SUCCESS;
343         default:
344             /* we don't know anything about this return value */
345             _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
346                      retval, username);
347             return PAM_SERVICE_ERR;
348     }
349     
350     /* should not be reached */
351     return PAM_IGNORE;
352 }
353
354
355 PAM_EXTERN
356 int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, 
357                      const char **argv)
358 {
359     int retval;
360     char *newpw, *oldpw;
361     const char *user;
362
363     /* Get name of a user */
364
365     retval = pam_get_user(pamh, &user, "Username: ");
366
367     if (retval != PAM_SUCCESS) {
368         return retval;
369     }
370
371     /* XXX check in domain format */
372
373     /* Perform preliminary check and store requested password for updating
374        later on */
375
376     if (flags & PAM_PRELIM_CHECK) {
377         struct pam_message msg[3], *pmsg[3];
378         struct pam_response *resp;
379
380         /* Converse to ensure we have the current password */
381
382         retval = auth_conversation(pamh);
383
384         if (retval != PAM_SUCCESS) {
385             return retval;
386         }
387
388         /* Obtain and verify current password */
389
390         pmsg[0] = &msg[0];
391         msg[0].msg_style = PAM_TEXT_INFO;
392         msg[0].msg = "Changing password for user %s";
393
394         pmsg[1] = &msg[1];
395         msg[1].msg_style = PAM_PROMPT_ECHO_OFF;
396         msg[1].msg = "New NT password: ";
397
398         pmsg[2] = &msg[2];
399         msg[2].msg_style = PAM_PROMPT_ECHO_OFF;
400         msg[2].msg = "Retype new NT password: ";
401
402         resp = NULL;
403
404         retval = converse(pamh, 3, pmsg, &resp);
405
406         if (resp != NULL) {
407
408             if (retval == PAM_SUCCESS) {
409
410                 /* Check password entered correctly */
411
412                 if (strcmp(resp[1].resp, resp[2].resp) != 0) { 
413                     struct pam_response *resp2;
414
415                     msg[0].msg_style = PAM_ERROR_MSG;
416                     msg[0].msg = "Sorry, passwords do not match";
417
418                     converse(pamh, 1, pmsg, &resp2);
419
420                     _pam_drop_reply(resp, 3);
421                     _pam_drop_reply(resp2, 1);
422
423                     return PAM_AUTHTOK_RECOVER_ERR;
424                 }
425
426                 /* Store passwords */
427
428                 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, resp[1].resp);
429                 _pam_drop_reply(resp, 3);
430             }
431         }
432
433         /* XXX What happens if root? */
434         /* XXX try first pass and use first pass args */
435
436         return retval;
437     }
438
439     if (flags & PAM_UPDATE_AUTHTOK) {
440
441         retval = pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **)&newpw);
442         if (retval != PAM_SUCCESS) {
443             return PAM_AUTHTOK_ERR;
444         }
445
446         retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&oldpw);
447         if (retval != PAM_SUCCESS) {
448             return PAM_AUTHTOK_ERR;
449         }
450
451         fprintf(stderr, "oldpw = %s, newpw = %s\n", oldpw, newpw);
452
453         if (retval == PAM_SUCCESS && 
454             winbind_chauthtok_request(user, oldpw, newpw) == 0) {
455             return PAM_SUCCESS;
456         }
457
458         return PAM_AUTHTOK_ERR;
459     }
460
461     return PAM_SERVICE_ERR;
462 }
463
464 #ifdef PAM_STATIC
465
466 /* static module data */
467
468 struct pam_module _pam_winbind_modstruct = {
469      MODULE_NAME,
470      pam_sm_authenticate,
471      pam_sm_setcred,
472      pam_sm_acct_mgmt,
473      NULL,
474      NULL,
475      pam_sm_chauthtok
476 };
477
478 #endif
479
480 /*
481  * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
482  * Copyright (c) Tim Potter      <tpot@samba.org>   2000
483  *
484  * Redistribution and use in source and binary forms, with or without
485  * modification, are permitted provided that the following conditions
486  * are met:
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.
496  *
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.)
502  *
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.
514  */