Imported Upstream version 1.6.6
[debian/sudo] / auth / pam.c
1 /*
2  * Copyright (c) 1999-2001 Todd C. Miller <Todd.Miller@courtesan.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * 4. Products derived from this software may not be called "Sudo" nor
20  *    may "Sudo" appear in their names without specific prior written
21  *    permission from the author.
22  *
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
26  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 #include "config.h"
36
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <stdio.h>
40 #ifdef STDC_HEADERS
41 # include <stdlib.h>
42 # include <stddef.h>
43 #else
44 # ifdef HAVE_STDLIB_H
45 #  include <stdlib.h>
46 # endif
47 #endif /* STDC_HEADERS */
48 #ifdef HAVE_STRING_H
49 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
50 #  include <memory.h>
51 # endif
52 # include <string.h>
53 #else
54 # ifdef HAVE_STRINGS_H
55 #  include <strings.h>
56 # endif
57 #endif /* HAVE_STRING_H */
58 #ifdef HAVE_UNISTD_H
59 # include <unistd.h>
60 #endif /* HAVE_UNISTD_H */
61 #include <pwd.h>
62
63 #include <security/pam_appl.h>
64
65 #include "sudo.h"
66 #include "sudo_auth.h"
67
68 #ifndef lint
69 static const char rcsid[] = "$Sudo: pam.c,v 1.29 2002/01/22 16:43:23 millert Exp $";
70 #endif /* lint */
71
72 static int sudo_conv __P((int, PAM_CONST struct pam_message **,
73                           struct pam_response **, VOID *));
74 static char *def_prompt;
75
76 #ifndef PAM_DATA_SILENT
77 #define PAM_DATA_SILENT 0
78 #endif
79
80 int
81 pam_init(pw, promptp, auth)
82     struct passwd *pw;
83     char **promptp;
84     sudo_auth *auth;
85 {
86     static struct pam_conv pam_conv;
87     pam_handle_t *pamh;
88
89     /* Initial PAM setup */
90     pam_conv.conv = sudo_conv;
91     if (pam_start("sudo", pw->pw_name, &pam_conv, &pamh) != PAM_SUCCESS) {
92         log_error(USE_ERRNO|NO_EXIT|NO_MAIL, 
93             "unable to initialize PAM");
94         return(AUTH_FATAL);
95     }
96     if (strcmp(user_tty, "unknown"))
97         (void) pam_set_item(pamh, PAM_TTY, user_tty);
98
99     auth->data = (VOID *) pamh;
100     return(AUTH_SUCCESS);
101 }
102
103 int
104 pam_verify(pw, prompt, auth)
105     struct passwd *pw;
106     char *prompt;
107     sudo_auth *auth;
108 {
109     int error;
110     const char *s;
111     pam_handle_t *pamh = (pam_handle_t *) auth->data;
112
113     def_prompt = prompt;        /* for sudo_conv */
114
115     /* PAM_SILENT prevents the authentication service from generating output. */
116     error = pam_authenticate(pamh, PAM_SILENT);
117     switch (error) {
118         case PAM_SUCCESS:
119             return(AUTH_SUCCESS);
120         case PAM_AUTH_ERR:
121         case PAM_MAXTRIES:
122             return(AUTH_FAILURE);
123         default:
124             if ((s = pam_strerror(pamh, error)))
125                 log_error(NO_EXIT|NO_MAIL, "pam_authenticate: %s", s);
126             return(AUTH_FATAL);
127     }
128 }
129
130 int
131 pam_cleanup(pw, auth)
132     struct passwd *pw;
133     sudo_auth *auth;
134 {
135     pam_handle_t *pamh = (pam_handle_t *) auth->data;
136     int status = PAM_DATA_SILENT;
137
138     /* Convert AUTH_FOO -> PAM_FOO as best we can. */
139     /* XXX - store real value somewhere in auth->data and use it */
140     switch (auth->status) {
141         case AUTH_SUCCESS:
142             status |= PAM_SUCCESS;
143             break;
144         case AUTH_FAILURE:
145             status |= PAM_AUTH_ERR;
146             break;
147         case AUTH_FATAL:
148         default:
149             status |= PAM_ABORT;
150             break;
151     }
152
153     if (pam_end(pamh, status) == PAM_SUCCESS)
154         return(AUTH_SUCCESS);
155     else
156         return(AUTH_FAILURE);
157 }
158
159 int
160 pam_prep_user(pw)
161     struct passwd *pw;
162 {
163     struct pam_conv pam_conv;
164     pam_handle_t *pamh;
165
166     /* We need to setup a new PAM session for the user we are changing *to*. */
167     pam_conv.conv = sudo_conv;
168     if (pam_start("sudo", pw->pw_name, &pam_conv, &pamh) != PAM_SUCCESS) {
169         log_error(USE_ERRNO|NO_EXIT|NO_MAIL, 
170             "unable to initialize PAM");
171         return(AUTH_FATAL);
172     }
173     (void) pam_set_item(pamh, PAM_RUSER, user_name);
174     if (strcmp(user_tty, "unknown"))
175         (void) pam_set_item(pamh, PAM_TTY, user_tty);
176
177     /*
178      * Set credentials (may include resource limits, device ownership, etc).
179      * We don't check the return value here because in Linux-PAM 0.75
180      * it returns the last saved return code, not the return code
181      * for the setcred module.  Because we haven't called pam_authenticate(),
182      * this is not set and so pam_setcred() returns PAM_PERM_DENIED.
183      * We can't call pam_acct_mgmt() with Linux-PAM for a similar reason.
184      */
185     (void) pam_setcred(pamh, PAM_ESTABLISH_CRED);
186
187     if (pam_end(pamh, PAM_SUCCESS) == PAM_SUCCESS)
188         return(PAM_SUCCESS);
189     else
190         return(AUTH_FAILURE);
191 }
192
193 /*
194  * ``Conversation function'' for PAM.
195  * XXX - does not handle PAM_BINARY_PROMPT
196  */
197 static int
198 sudo_conv(num_msg, msg, response, appdata_ptr)
199     int num_msg;
200     PAM_CONST struct pam_message **msg;
201     struct pam_response **response;
202     VOID *appdata_ptr;
203 {
204     struct pam_response *pr;
205     PAM_CONST struct pam_message *pm;
206     const char *p = def_prompt;
207     char *pass;
208     int n;
209     extern int nil_pw;
210
211     if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL)
212         return(PAM_CONV_ERR);
213     (void) memset(*response, 0, num_msg * sizeof(struct pam_response));
214
215     for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) {
216         switch (pm->msg_style) {
217             case PAM_PROMPT_ECHO_ON:
218                 tgetpass_flags |= TGP_ECHO;
219             case PAM_PROMPT_ECHO_OFF:
220                 /* Only override PAM prompt if it matches /^Password: ?/ */
221                 if (strncmp(pm->msg, "Password:", 9) || (pm->msg[9] != '\0'
222                     && (pm->msg[9] != ' ' || pm->msg[10] != '\0')))
223                     p = pm->msg;
224                 /* Read the password. */
225                 pass = tgetpass(p, def_ival(I_PASSWD_TIMEOUT) * 60,
226                     tgetpass_flags);
227                 pr->resp = estrdup(pass ? pass : "");
228                 if (*pr->resp == '\0')
229                     nil_pw = 1;         /* empty password */
230                 else
231                     memset(pass, 0, strlen(pass));
232                 break;
233             case PAM_TEXT_INFO:
234                 if (pm->msg)
235                     (void) puts(pm->msg);
236                 break;
237             case PAM_ERROR_MSG:
238                 if (pm->msg) {
239                     (void) fputs(pm->msg, stderr);
240                     (void) fputc('\n', stderr);
241                 }
242                 break;
243             default:
244                 /* Zero and free allocated memory and return an error. */
245                 for (pr = *response, n = num_msg; n--; pr++) {
246                     if (pr->resp != NULL) {
247                         (void) memset(pr->resp, 0, strlen(pr->resp));
248                         free(pr->resp);
249                         pr->resp = NULL;
250                     }
251                 }
252                 (void) memset(*response, 0,
253                     num_msg * sizeof(struct pam_response));
254                 free(*response);
255                 *response = NULL;
256                 return(PAM_CONV_ERR);
257         }
258     }
259
260     return(PAM_SUCCESS);
261 }