source: trunk/third/openssh/auth-pam.c @ 18763

Revision 18763, 13.0 KB checked in by zacheiss, 22 years ago (diff)
Merge openssh 3.5p1.
Line 
1/*
2 * Copyright (c) 2000 Damien Miller.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef USE_PAM
28#include "xmalloc.h"
29#include "log.h"
30#include "auth.h"
31#include "auth-options.h"
32#include "auth-pam.h"
33#include "servconf.h"
34#include "canohost.h"
35#include "readpass.h"
36
37extern char *__progname;
38
39extern int use_privsep;
40
41RCSID("$Id: auth-pam.c,v 1.3 2003-02-06 03:45:44 zacheiss Exp $");
42
43#define NEW_AUTHTOK_MSG \
44        "Warning: Your password has expired, please change it now."
45#define NEW_AUTHTOK_MSG_PRIVSEP \
46        "Your password has expired, the session cannot proceed."
47
48static int do_pam_conversation(int num_msg, const struct pam_message **msg,
49        struct pam_response **resp, void *appdata_ptr);
50
51/* module-local variables */
52static struct pam_conv conv = {
53        (int (*)())do_pam_conversation,
54        NULL
55};
56static char *__pam_msg = NULL;
57static pam_handle_t *__pamh = NULL;
58static const char *__pampasswd = NULL;
59
60/* states for do_pam_conversation() */
61enum { INITIAL_LOGIN, OTHER } pamstate = INITIAL_LOGIN;
62/* remember whether pam_acct_mgmt() returned PAM_NEW_AUTHTOK_REQD */
63static int password_change_required = 0;
64/* remember whether the last pam_authenticate() succeeded or not */
65static int was_authenticated = 0;
66
67/* Remember what has been initialised */
68static int session_opened = 0;
69static int creds_set = 0;
70
71/* accessor which allows us to switch conversation structs according to
72 * the authentication method being used */
73void do_pam_set_conv(struct pam_conv *conv)
74{
75        pam_set_item(__pamh, PAM_CONV, conv);
76}
77
78/* start an authentication run */
79int do_pam_authenticate(int flags)
80{
81        int retval = pam_authenticate(__pamh, flags);
82        was_authenticated = (retval == PAM_SUCCESS);
83        return retval;
84}
85
86/*
87 * PAM conversation function.
88 * There are two states this can run in.
89 *
90 * INITIAL_LOGIN mode simply feeds the password from the client into
91 * PAM in response to PAM_PROMPT_ECHO_OFF, and collects output
92 * messages with into __pam_msg.  This is used during initial
93 * authentication to bypass the normal PAM password prompt.
94 *
95 * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase()
96 * and outputs messages to stderr. This mode is used if pam_chauthtok()
97 * is called to update expired passwords.
98 */
99static int do_pam_conversation(int num_msg, const struct pam_message **msg,
100        struct pam_response **resp, void *appdata_ptr)
101{
102        struct pam_response *reply;
103        int count;
104        char buf[1024];
105
106        /* PAM will free this later */
107        reply = xmalloc(num_msg * sizeof(*reply));
108
109        for (count = 0; count < num_msg; count++) {
110                if (pamstate == INITIAL_LOGIN) {
111                        /*
112                         * We can't use stdio yet, queue messages for
113                         * printing later
114                         */
115                        switch(PAM_MSG_MEMBER(msg, count, msg_style)) {
116                        case PAM_PROMPT_ECHO_ON:
117                                xfree(reply);
118                                return PAM_CONV_ERR;
119                        case PAM_PROMPT_ECHO_OFF:
120                                if (__pampasswd == NULL) {
121                                        xfree(reply);
122                                        return PAM_CONV_ERR;
123                                }
124                                reply[count].resp = xstrdup(__pampasswd);
125                                reply[count].resp_retcode = PAM_SUCCESS;
126                                break;
127                        case PAM_ERROR_MSG:
128                        case PAM_TEXT_INFO:
129                                if (PAM_MSG_MEMBER(msg, count, msg) != NULL) {
130                                        message_cat(&__pam_msg,
131                                            PAM_MSG_MEMBER(msg, count, msg));
132                                }
133                                reply[count].resp = xstrdup("");
134                                reply[count].resp_retcode = PAM_SUCCESS;
135                                break;
136                        default:
137                                xfree(reply);
138                                return PAM_CONV_ERR;
139                        }
140                } else {
141                        /*
142                         * stdio is connected, so interact directly
143                         */
144                        switch(PAM_MSG_MEMBER(msg, count, msg_style)) {
145                        case PAM_PROMPT_ECHO_ON:
146                                fputs(PAM_MSG_MEMBER(msg, count, msg), stderr);
147                                fgets(buf, sizeof(buf), stdin);
148                                reply[count].resp = xstrdup(buf);
149                                reply[count].resp_retcode = PAM_SUCCESS;
150                                break;
151                        case PAM_PROMPT_ECHO_OFF:
152                                reply[count].resp =
153                                    read_passphrase(PAM_MSG_MEMBER(msg, count,
154                                        msg), RP_ALLOW_STDIN);
155                                reply[count].resp_retcode = PAM_SUCCESS;
156                                break;
157                        case PAM_ERROR_MSG:
158                        case PAM_TEXT_INFO:
159                                if (PAM_MSG_MEMBER(msg, count, msg) != NULL)
160                                        fprintf(stderr, "%s\n",
161                                            PAM_MSG_MEMBER(msg, count, msg));
162                                reply[count].resp = xstrdup("");
163                                reply[count].resp_retcode = PAM_SUCCESS;
164                                break;
165                        default:
166                                xfree(reply);
167                                return PAM_CONV_ERR;
168                        }
169                }
170        }
171
172        *resp = reply;
173
174        return PAM_SUCCESS;
175}
176
177/* Called at exit to cleanly shutdown PAM */
178void do_pam_cleanup_proc(void *context)
179{
180        int pam_retval = PAM_SUCCESS;
181
182        if (__pamh && session_opened) {
183                pam_retval = pam_close_session(__pamh, 0);
184                if (pam_retval != PAM_SUCCESS)
185                        log("Cannot close PAM session[%d]: %.200s",
186                            pam_retval, PAM_STRERROR(__pamh, pam_retval));
187        }
188
189        if (__pamh && creds_set) {
190                pam_retval = pam_setcred(__pamh, PAM_DELETE_CRED);
191                if (pam_retval != PAM_SUCCESS)
192                        debug("Cannot delete credentials[%d]: %.200s",
193                            pam_retval, PAM_STRERROR(__pamh, pam_retval));
194        }
195
196        if (__pamh) {
197                pam_retval = pam_end(__pamh, pam_retval);
198                if (pam_retval != PAM_SUCCESS)
199                        log("Cannot release PAM authentication[%d]: %.200s",
200                            pam_retval, PAM_STRERROR(__pamh, pam_retval));
201        }
202}
203
204/* Attempt password authentation using PAM */
205int auth_pam_password(Authctxt *authctxt, const char *password)
206{
207        extern ServerOptions options;
208        int pam_retval;
209        struct passwd *pw = authctxt->pw;
210
211        do_pam_set_conv(&conv);
212
213        /* deny if no user. */
214        if (pw == NULL)
215                return 0;
216        if (pw->pw_uid == 0 && options.permit_root_login == PERMIT_NO_PASSWD)
217                return 0;
218        if (*password == '\0' && options.permit_empty_passwd == 0)
219                return 0;
220
221        __pampasswd = password;
222
223        pamstate = INITIAL_LOGIN;
224        pam_retval = do_pam_authenticate(
225            options.permit_empty_passwd == 0 ? PAM_DISALLOW_NULL_AUTHTOK : 0);
226        if (pam_retval == PAM_SUCCESS) {
227                debug("PAM Password authentication accepted for "
228                    "user \"%.100s\"", pw->pw_name);
229                return 1;
230        } else {
231                debug("PAM Password authentication for \"%.100s\" "
232                    "failed[%d]: %s", pw->pw_name, pam_retval,
233                    PAM_STRERROR(__pamh, pam_retval));
234                return 0;
235        }
236}
237
238/* Do account management using PAM */
239int do_pam_account(char *username, char *remote_user)
240{
241        int pam_retval;
242
243        do_pam_set_conv(&conv);
244
245        if (remote_user) {
246                debug("PAM setting ruser to \"%.200s\"", remote_user);
247                pam_retval = pam_set_item(__pamh, PAM_RUSER, remote_user);
248                if (pam_retval != PAM_SUCCESS)
249                        fatal("PAM set ruser failed[%d]: %.200s", pam_retval,
250                            PAM_STRERROR(__pamh, pam_retval));
251        }
252
253        pam_retval = pam_acct_mgmt(__pamh, 0);
254        debug2("pam_acct_mgmt() = %d", pam_retval);
255        switch (pam_retval) {
256                case PAM_SUCCESS:
257                        /* This is what we want */
258                        break;
259#if 0
260                case PAM_NEW_AUTHTOK_REQD:
261                        message_cat(&__pam_msg, use_privsep ?
262                            NEW_AUTHTOK_MSG_PRIVSEP : NEW_AUTHTOK_MSG);
263                        /* flag that password change is necessary */
264                        password_change_required = 1;
265                        /* disallow other functionality for now */
266                        no_port_forwarding_flag |= 2;
267                        no_agent_forwarding_flag |= 2;
268                        no_x11_forwarding_flag |= 2;
269                        break;
270#endif
271                default:
272                        log("PAM rejected by account configuration[%d]: "
273                            "%.200s", pam_retval, PAM_STRERROR(__pamh,
274                            pam_retval));
275                        return(0);
276        }
277
278        return(1);
279}
280
281/* Do PAM-specific session initialisation */
282void do_pam_session(char *username, const char *ttyname)
283{
284        int pam_retval;
285
286        do_pam_set_conv(&conv);
287
288        if (ttyname != NULL) {
289                debug("PAM setting tty to \"%.200s\"", ttyname);
290                pam_retval = pam_set_item(__pamh, PAM_TTY, ttyname);
291                if (pam_retval != PAM_SUCCESS)
292                        fatal("PAM set tty failed[%d]: %.200s",
293                            pam_retval, PAM_STRERROR(__pamh, pam_retval));
294        }
295
296        pam_retval = pam_open_session(__pamh, 0);
297        if (pam_retval != PAM_SUCCESS)
298                fatal("PAM session setup failed[%d]: %.200s",
299                    pam_retval, PAM_STRERROR(__pamh, pam_retval));
300
301        session_opened = 1;
302}
303
304/* Set PAM credentials */
305void do_pam_setcred(int init)
306{
307        int pam_retval;
308
309        if (__pamh == NULL)
310                return;
311
312        do_pam_set_conv(&conv);
313
314        debug("PAM establishing creds");
315        pam_retval = pam_setcred(__pamh,
316            init ? PAM_ESTABLISH_CRED : PAM_REINITIALIZE_CRED);
317        if (pam_retval != PAM_SUCCESS) {
318                if (was_authenticated)
319                        fatal("PAM setcred failed[%d]: %.200s",
320                            pam_retval, PAM_STRERROR(__pamh, pam_retval));
321                else
322                        debug("PAM setcred failed[%d]: %.200s",
323                            pam_retval, PAM_STRERROR(__pamh, pam_retval));
324        } else
325                creds_set = 1;
326}
327
328/* accessor function for file scope static variable */
329int is_pam_password_change_required(void)
330{
331        return password_change_required;
332}
333
334/*
335 * Have user change authentication token if pam_acct_mgmt() indicated
336 * it was expired.  This needs to be called after an interactive
337 * session is established and the user's pty is connected to
338 * stdin/stdout/stderr.
339 */
340void do_pam_chauthtok(void)
341{
342        int pam_retval;
343
344        do_pam_set_conv(&conv);
345
346        if (password_change_required) {
347                if (use_privsep)
348                        fatal("Password changing is currently unsupported"
349                            " with privilege separation");
350                pamstate = OTHER;
351                pam_retval = pam_chauthtok(__pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
352                if (pam_retval != PAM_SUCCESS)
353                        fatal("PAM pam_chauthtok failed[%d]: %.200s",
354                            pam_retval, PAM_STRERROR(__pamh, pam_retval));
355#if 0
356                /* XXX: This would need to be done in the parent process,
357                 * but there's currently no way to pass such request. */
358                no_port_forwarding_flag &= ~2;
359                no_agent_forwarding_flag &= ~2;
360                no_x11_forwarding_flag &= ~2;
361                if (!no_port_forwarding_flag && options.allow_tcp_forwarding)
362                        channel_permit_all_opens();
363#endif
364        }
365}
366
367/* Cleanly shutdown PAM */
368void finish_pam(void)
369{
370        do_pam_cleanup_proc(NULL);
371        fatal_remove_cleanup(&do_pam_cleanup_proc, NULL);
372}
373
374/* Start PAM authentication for specified account */
375void start_pam(const char *user)
376{
377        int pam_retval;
378        extern ServerOptions options;
379        extern u_int utmp_len;
380        const char *rhost;
381
382        debug("Starting up PAM with username \"%.200s\"", user);
383
384        pam_retval = pam_start(SSHD_PAM_SERVICE, user, &conv, &__pamh);
385
386        if (pam_retval != PAM_SUCCESS)
387                fatal("PAM initialisation failed[%d]: %.200s",
388                    pam_retval, PAM_STRERROR(__pamh, pam_retval));
389
390        rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping);
391        debug("PAM setting rhost to \"%.200s\"", rhost);
392
393        pam_retval = pam_set_item(__pamh, PAM_RHOST, rhost);
394        if (pam_retval != PAM_SUCCESS)
395                fatal("PAM set rhost failed[%d]: %.200s", pam_retval,
396                    PAM_STRERROR(__pamh, pam_retval));
397#ifdef PAM_TTY_KLUDGE
398        /*
399         * Some PAM modules (e.g. pam_time) require a TTY to operate,
400         * and will fail in various stupid ways if they don't get one.
401         * sshd doesn't set the tty until too late in the auth process and may
402         * not even need one (for tty-less connections)
403         * Kludge: Set a fake PAM_TTY
404         */
405        pam_retval = pam_set_item(__pamh, PAM_TTY, "NODEVssh");
406        if (pam_retval != PAM_SUCCESS)
407                fatal("PAM set tty failed[%d]: %.200s",
408                    pam_retval, PAM_STRERROR(__pamh, pam_retval));
409#endif /* PAM_TTY_KLUDGE */
410
411        fatal_add_cleanup(&do_pam_cleanup_proc, NULL);
412}
413
414/* Return list of PAM environment strings */
415char **fetch_pam_environment(void)
416{
417#ifdef HAVE_PAM_GETENVLIST
418        return(pam_getenvlist(__pamh));
419#else /* HAVE_PAM_GETENVLIST */
420        return(NULL);
421#endif /* HAVE_PAM_GETENVLIST */
422}
423
424/* Set a PAM environment string. We need to do this so that the session
425 * modules can handle things like Kerberos/GSI credentials that appear
426 * during the ssh authentication process.
427 */
428
429int do_pam_putenv(char *name, char *value) {
430        char *compound;
431        int ret=1;
432       
433        compound=xmalloc(strlen(name)+strlen(value)+2);
434        if (compound) {
435                sprintf(compound,"%s=%s",name,value);
436                ret=pam_putenv(__pamh,compound);
437                xfree(compound);
438        }
439        return(ret);
440}
441
442void free_pam_environment(char **env)
443{
444        int i;
445
446        if (env != NULL) {
447                for (i = 0; env[i] != NULL; i++)
448                        xfree(env[i]);
449        }
450}
451
452/* Print any messages that have been generated during authentication */
453/* or account checking to stderr */
454void print_pam_messages(void)
455{
456        if (__pam_msg != NULL)
457                fputs(__pam_msg, stderr);
458}
459
460/* Append a message to buffer */
461void message_cat(char **p, const char *a)
462{
463        char *cp;
464        size_t new_len;
465
466        new_len = strlen(a);
467
468        if (*p) {
469                size_t len = strlen(*p);
470
471                *p = xrealloc(*p, new_len + len + 2);
472                cp = *p + len;
473        } else
474                *p = cp = xmalloc(new_len + 2);
475
476        memcpy(cp, a, new_len);
477        cp[new_len] = '\n';
478        cp[new_len + 1] = '\0';
479}
480
481#endif /* USE_PAM */
Note: See TracBrowser for help on using the repository browser.