~ubuntu-branches/ubuntu/quantal/sudo/quantal

« back to all changes in this revision

Viewing changes to plugins/sudoers/auth/pam.c

  • Committer: Package Import Robot
  • Author(s): Marc Deslauriers
  • Date: 2011-11-20 12:07:45 UTC
  • mfrom: (1.3.17 sid)
  • Revision ID: package-import@ubuntu.com-20111120120745-o3qpklobmygytndc
Tags: 1.8.3p1-1ubuntu1
* Merge from debian/testing, remaining changes:
  - debian/patches/keep_home_by_default.patch:
    + Set HOME in initial_keepenv_table. (rebased for 1.8.3p1)
  - debian/patches/enable_badpass.patch: turn on "mail_badpass" by default:
    + attempting sudo without knowing a login password is as bad as not
      being listed in the sudoers file, especially if getting the password
      wrong means doing the access-check-email-notification never happens
      (rebased for 1.8.3p1)
  - debian/rules:
    + compile with --without-lecture --with-tty-tickets (Ubuntu specific)
    + install man/man8/sudo_root.8 (Ubuntu specific)
    + install apport hooks
    + The ubuntu-sudo-as-admin-successful.patch was taken upstream by
      Debian however it requires a --enable-admin-flag configure flag to
      actually enable it.
  - debian/sudoers: 
    + grant admin group sudo access
  - debian/sudo-ldap.dirs, debian/sudo.dirs: 
    + add usr/share/apport/package-hooks
  - debian/sudo.preinst:
    + avoid conffile prompt by checking for known default /etc/sudoers
      and if found installing the correct default /etc/sudoers file

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 1999-2005, 2007-2011 Todd C. Miller <Todd.Miller@courtesan.com>
 
3
 *
 
4
 * Permission to use, copy, modify, and distribute this software for any
 
5
 * purpose with or without fee is hereby granted, provided that the above
 
6
 * copyright notice and this permission notice appear in all copies.
 
7
 *
 
8
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 
9
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 
10
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 
11
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 
12
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 
13
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 
14
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
15
 *
 
16
 * Sponsored in part by the Defense Advanced Research Projects
 
17
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
 
18
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
 
19
 */
 
20
 
 
21
#include <config.h>
 
22
 
 
23
#include <sys/types.h>
 
24
#include <sys/param.h>
 
25
#include <stdio.h>
 
26
#ifdef STDC_HEADERS
 
27
# include <stdlib.h>
 
28
# include <stddef.h>
 
29
#else
 
30
# ifdef HAVE_STDLIB_H
 
31
#  include <stdlib.h>
 
32
# endif
 
33
#endif /* STDC_HEADERS */
 
34
#ifdef HAVE_STRING_H
 
35
# include <string.h>
 
36
#endif /* HAVE_STRING_H */
 
37
#ifdef HAVE_STRINGS_H
 
38
# include <strings.h>
 
39
#endif /* HAVE_STRINGS_H */
 
40
#ifdef HAVE_UNISTD_H
 
41
# include <unistd.h>
 
42
#endif /* HAVE_UNISTD_H */
 
43
#include <pwd.h>
 
44
#include <errno.h>
 
45
 
 
46
#ifdef HAVE_PAM_PAM_APPL_H
 
47
# include <pam/pam_appl.h>
 
48
#else
 
49
# include <security/pam_appl.h>
 
50
#endif
 
51
 
 
52
#ifdef HAVE_LIBINTL_H
 
53
# if defined(__LINUX_PAM__)
 
54
#  define PAM_TEXT_DOMAIN       "Linux-PAM"
 
55
# elif defined(__sun__)
 
56
#  define PAM_TEXT_DOMAIN       "SUNW_OST_SYSOSPAM"
 
57
# endif
 
58
#endif
 
59
 
 
60
#include "sudoers.h"
 
61
#include "sudo_auth.h"
 
62
 
 
63
/* Only OpenPAM and Linux PAM use const qualifiers. */
 
64
#if defined(_OPENPAM) || defined(OPENPAM_VERSION) || \
 
65
    defined(__LIBPAM_VERSION) || defined(__LINUX_PAM__)
 
66
# define PAM_CONST      const
 
67
#else
 
68
# define PAM_CONST
 
69
#endif
 
70
 
 
71
static int converse(int, PAM_CONST struct pam_message **,
 
72
                    struct pam_response **, void *);
 
73
static char *def_prompt = "Password:";
 
74
static int gotintr;
 
75
 
 
76
#ifndef PAM_DATA_SILENT
 
77
#define PAM_DATA_SILENT 0
 
78
#endif
 
79
 
 
80
static pam_handle_t *pamh;
 
81
 
 
82
int
 
83
pam_init(struct passwd *pw, sudo_auth *auth)
 
84
{
 
85
    static struct pam_conv pam_conv;
 
86
    static int pam_status;
 
87
 
 
88
    /* Initial PAM setup */
 
89
    if (auth != NULL)
 
90
        auth->data = (void *) &pam_status;
 
91
    pam_conv.conv = converse;
 
92
#ifdef HAVE_PAM_LOGIN
 
93
    if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
 
94
        pam_status = pam_start("sudo-i", pw->pw_name, &pam_conv, &pamh);
 
95
    else
 
96
#endif
 
97
        pam_status = pam_start("sudo", pw->pw_name, &pam_conv, &pamh);
 
98
    if (pam_status != PAM_SUCCESS) {
 
99
        log_error(USE_ERRNO|NO_EXIT|NO_MAIL, _("unable to initialize PAM"));
 
100
        return AUTH_FATAL;
 
101
    }
 
102
 
 
103
    /*
 
104
     * Set PAM_RUSER to the invoking user (the "from" user).
 
105
     * We set PAM_RHOST to avoid a bug in Solaris 7 and below.
 
106
     */
 
107
    (void) pam_set_item(pamh, PAM_RUSER, user_name);
 
108
#ifdef __sun__
 
109
    (void) pam_set_item(pamh, PAM_RHOST, user_host);
 
110
#endif
 
111
 
 
112
    /*
 
113
     * Some versions of pam_lastlog have a bug that
 
114
     * will cause a crash if PAM_TTY is not set so if
 
115
     * there is no tty, set PAM_TTY to the empty string.
 
116
     */
 
117
    if (user_ttypath == NULL)
 
118
        (void) pam_set_item(pamh, PAM_TTY, "");
 
119
    else
 
120
        (void) pam_set_item(pamh, PAM_TTY, user_ttypath);
 
121
 
 
122
    return AUTH_SUCCESS;
 
123
}
 
124
 
 
125
int
 
126
pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth)
 
127
{
 
128
    const char *s;
 
129
    int *pam_status = (int *) auth->data;
 
130
 
 
131
    def_prompt = prompt;        /* for converse */
 
132
 
 
133
    /* PAM_SILENT prevents the authentication service from generating output. */
 
134
    *pam_status = pam_authenticate(pamh, PAM_SILENT);
 
135
    switch (*pam_status) {
 
136
        case PAM_SUCCESS:
 
137
            *pam_status = pam_acct_mgmt(pamh, PAM_SILENT);
 
138
            switch (*pam_status) {
 
139
                case PAM_SUCCESS:
 
140
                    return AUTH_SUCCESS;
 
141
                case PAM_AUTH_ERR:
 
142
                    log_error(NO_EXIT|NO_MAIL, _("account validation failure, "
 
143
                        "is your account locked?"));
 
144
                    return AUTH_FATAL;
 
145
                case PAM_NEW_AUTHTOK_REQD:
 
146
                    log_error(NO_EXIT|NO_MAIL, _("Account or password is "
 
147
                        "expired, reset your password and try again"));
 
148
                    *pam_status = pam_chauthtok(pamh,
 
149
                        PAM_CHANGE_EXPIRED_AUTHTOK);
 
150
                    if (*pam_status == PAM_SUCCESS)
 
151
                        return AUTH_SUCCESS;
 
152
                    if ((s = pam_strerror(pamh, *pam_status)))
 
153
                        log_error(NO_EXIT|NO_MAIL, _("pam_chauthtok: %s"), s);
 
154
                    return AUTH_FAILURE;
 
155
                case PAM_AUTHTOK_EXPIRED:
 
156
                    log_error(NO_EXIT|NO_MAIL,
 
157
                        _("Password expired, contact your system administrator"));
 
158
                    return AUTH_FATAL;
 
159
                case PAM_ACCT_EXPIRED:
 
160
                    log_error(NO_EXIT|NO_MAIL,
 
161
                        _("Account expired or PAM config lacks an \"account\" "
 
162
                        "section for sudo, contact your system administrator"));
 
163
                    return AUTH_FATAL;
 
164
            }
 
165
            /* FALLTHROUGH */
 
166
        case PAM_AUTH_ERR:
 
167
            if (gotintr) {
 
168
                /* error or ^C from tgetpass() */
 
169
                return AUTH_INTR;
 
170
            }
 
171
        case PAM_MAXTRIES:
 
172
        case PAM_PERM_DENIED:
 
173
            return AUTH_FAILURE;
 
174
        default:
 
175
            if ((s = pam_strerror(pamh, *pam_status)))
 
176
                log_error(NO_EXIT|NO_MAIL, _("pam_authenticate: %s"), s);
 
177
            return AUTH_FATAL;
 
178
    }
 
179
}
 
180
 
 
181
int
 
182
pam_cleanup(struct passwd *pw, sudo_auth *auth)
 
183
{
 
184
    int *pam_status = (int *) auth->data;
 
185
 
 
186
    /* If successful, we can't close the session until pam_end_session() */
 
187
    if (*pam_status == AUTH_SUCCESS)
 
188
        return AUTH_SUCCESS;
 
189
 
 
190
    *pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT);
 
191
    pamh = NULL;
 
192
    return *pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE;
 
193
}
 
194
 
 
195
int
 
196
pam_begin_session(struct passwd *pw, sudo_auth *auth)
 
197
{
 
198
    int status = PAM_SUCCESS;
 
199
 
 
200
    /*
 
201
     * If there is no valid user we cannot open a PAM session.
 
202
     * This is not an error as sudo can run commands with arbitrary
 
203
     * uids, it just means we are done from a session management standpoint.
 
204
     */
 
205
    if (pw == NULL) {
 
206
        if (pamh != NULL) {
 
207
            (void) pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);
 
208
            pamh = NULL;
 
209
        }
 
210
        goto done;
 
211
    }
 
212
 
 
213
    /*
 
214
     * Update PAM_USER to reference the user we are running the command
 
215
     * as, as opposed to the user we authenticated as.
 
216
     */
 
217
    (void) pam_set_item(pamh, PAM_USER, pw->pw_name);
 
218
 
 
219
    /*
 
220
     * Set credentials (may include resource limits, device ownership, etc).
 
221
     * We don't check the return value here because in Linux-PAM 0.75
 
222
     * it returns the last saved return code, not the return code
 
223
     * for the setcred module.  Because we haven't called pam_authenticate(),
 
224
     * this is not set and so pam_setcred() returns PAM_PERM_DENIED.
 
225
     * We can't call pam_acct_mgmt() with Linux-PAM for a similar reason.
 
226
     */
 
227
    (void) pam_setcred(pamh, PAM_ESTABLISH_CRED);
 
228
 
 
229
#ifndef NO_PAM_SESSION
 
230
    status = pam_open_session(pamh, 0);
 
231
    if (status != PAM_SUCCESS) {
 
232
        (void) pam_end(pamh, status | PAM_DATA_SILENT);
 
233
        pamh = NULL;
 
234
    }
 
235
#endif
 
236
 
 
237
done:
 
238
    return status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE;
 
239
}
 
240
 
 
241
int
 
242
pam_end_session(struct passwd *pw, sudo_auth *auth)
 
243
{
 
244
    int status = PAM_SUCCESS;
 
245
 
 
246
    if (pamh != NULL) {
 
247
#ifndef NO_PAM_SESSION
 
248
        /*
 
249
         * Update PAM_USER to reference the user we are running the command
 
250
         * as to match the call to pam_open_session().
 
251
         */
 
252
        (void) pam_set_item(pamh, PAM_USER, pw->pw_name);
 
253
        (void) pam_close_session(pamh, PAM_SILENT);
 
254
#endif
 
255
        status = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);
 
256
        pamh = NULL;
 
257
    }
 
258
 
 
259
    return status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE;
 
260
}
 
261
 
 
262
/*
 
263
 * ``Conversation function'' for PAM.
 
264
 * XXX - does not handle PAM_BINARY_PROMPT
 
265
 */
 
266
static int
 
267
converse(int num_msg, PAM_CONST struct pam_message **msg,
 
268
    struct pam_response **response, void *appdata_ptr)
 
269
{
 
270
    struct pam_response *pr;
 
271
    PAM_CONST struct pam_message *pm;
 
272
    const char *prompt;
 
273
    char *pass;
 
274
    int n, type, std_prompt;
 
275
 
 
276
    if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL)
 
277
        return PAM_SYSTEM_ERR;
 
278
    zero_bytes(*response, num_msg * sizeof(struct pam_response));
 
279
 
 
280
    for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) {
 
281
        type = SUDO_CONV_PROMPT_ECHO_OFF;
 
282
        switch (pm->msg_style) {
 
283
            case PAM_PROMPT_ECHO_ON:
 
284
                type = SUDO_CONV_PROMPT_ECHO_ON;
 
285
            case PAM_PROMPT_ECHO_OFF:
 
286
                prompt = def_prompt;
 
287
 
 
288
                /* Error out if the last password read was interrupted. */
 
289
                if (gotintr)
 
290
                    goto err;
 
291
 
 
292
                /* Is the sudo prompt standard? (If so, we'l just use PAM's) */
 
293
                std_prompt =  strncmp(def_prompt, "Password:", 9) == 0 &&
 
294
                    (def_prompt[9] == '\0' ||
 
295
                    (def_prompt[9] == ' ' && def_prompt[10] == '\0'));
 
296
 
 
297
                /* Only override PAM prompt if it matches /^Password: ?/ */
 
298
#if defined(PAM_TEXT_DOMAIN) && defined(HAVE_LIBINTL_H)
 
299
                if (!def_passprompt_override && (std_prompt ||
 
300
                    (strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password: ")) &&
 
301
                    strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password:")))))
 
302
                    prompt = pm->msg;
 
303
#else
 
304
                if (!def_passprompt_override && (std_prompt ||
 
305
                    strncmp(pm->msg, "Password:", 9) || (pm->msg[9] != '\0'
 
306
                    && (pm->msg[9] != ' ' || pm->msg[10] != '\0'))))
 
307
                    prompt = pm->msg;
 
308
#endif
 
309
                /* Read the password unless interrupted. */
 
310
                pass = auth_getpass(prompt, def_passwd_timeout * 60, type);
 
311
                if (pass == NULL) {
 
312
                    /* We got ^C instead of a password; abort quickly. */
 
313
                    if (errno == EINTR)
 
314
                        gotintr = 1;
 
315
#if defined(__darwin__) || defined(__APPLE__)
 
316
                    pass = "";
 
317
#else
 
318
                    goto err;
 
319
#endif
 
320
                }
 
321
                pr->resp = estrdup(pass);
 
322
                zero_bytes(pass, strlen(pass));
 
323
                break;
 
324
            case PAM_TEXT_INFO:
 
325
                if (pm->msg)
 
326
                    (void) puts(pm->msg);
 
327
                break;
 
328
            case PAM_ERROR_MSG:
 
329
                if (pm->msg) {
 
330
                    (void) fputs(pm->msg, stderr);
 
331
                    (void) fputc('\n', stderr);
 
332
                }
 
333
                break;
 
334
            default:
 
335
                goto err;
 
336
        }
 
337
    }
 
338
 
 
339
    return PAM_SUCCESS;
 
340
 
 
341
err:
 
342
    /* Zero and free allocated memory and return an error. */
 
343
    for (pr = *response, n = num_msg; n--; pr++) {
 
344
        if (pr->resp != NULL) {
 
345
            zero_bytes(pr->resp, strlen(pr->resp));
 
346
            free(pr->resp);
 
347
            pr->resp = NULL;
 
348
        }
 
349
    }
 
350
    zero_bytes(*response, num_msg * sizeof(struct pam_response));
 
351
    free(*response);
 
352
    *response = NULL;
 
353
    return gotintr ? PAM_AUTH_ERR : PAM_CONV_ERR;
 
354
}