~ubuntu-branches/ubuntu/quantal/libpam-krb5/quantal-201405130549

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
 * Implements the PAM password group API (pam_sm_chauthtok).
 *
 * Copyright 2005, 2006, 2007, 2008, 2009 Russ Allbery <rra@stanford.edu>
 * Copyright 2005 Andres Salomon <dilinger@debian.org>
 * Copyright 1999, 2000 Frank Cusack <fcusack@fcusack.com>
 *
 * See LICENSE for licensing terms.
 */

/* Get declarations for the password functions. */
#define PAM_SM_PASSWORD

#include <config.h>
#include <portable/pam.h>

#include <errno.h>
#include <string.h>

#include <internal.h>


/*
 * The main PAM interface for password changing.
 */
int
pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
    struct context *ctx = NULL;
    struct pam_args *args;
    int pamret = PAM_SUCCESS;
    int status;
    PAM_CONST char *user;
    char *pass = NULL;

    args = pamk5_args_parse(pamh, flags, argc, argv);
    if (args == NULL) {
        pamk5_crit(NULL, "cannot allocate memory: %s", strerror(errno));
        pamret = PAM_AUTHTOK_ERR;
        goto done;
    }
    pamret = pamk5_context_fetch(args);
    ENTRY(args, flags);

    /* We only support password changes. */
    if (!(flags & PAM_UPDATE_AUTHTOK) && !(flags & PAM_PRELIM_CHECK)) {
        pamk5_err(args, "invalid pam_chauthtok flags %d", flags);
        pamret = PAM_AUTHTOK_ERR;
        goto done;
    }

    /*
     * Check whether we should ignore this user.
     *
     * If we do ignore this user, and we're not in the preliminary check
     * phase, still prompt the user for the new password, but suppress our
     * banner.  This is a little strange, but it allows another module to be
     * stacked behind pam-krb5 with use_authtok and have it still work for
     * ignored users.
     *
     * We ignore the return status when prompting for the new password in this
     * case.  The worst thing that can happen is to fail to get the password,
     * in which case the other module will fail (or might even not care).
     */
    if (args->ignore_root || args->minimum_uid > 0) {
        status = pam_get_user(pamh, &user, NULL);
        if (status == PAM_SUCCESS && pamk5_should_ignore(args, user)) {
            if (flags & PAM_UPDATE_AUTHTOK) {
                if (args->banner != NULL) {
                    free(args->banner);
                    args->banner = NULL;
                }
                pamk5_password_prompt(args, NULL);
            }
            pamret = PAM_IGNORE;
            goto done;
        }
    }

    /*
     * If we weren't able to find an existing context to use, we're going
     * into this fresh and need to create a new context.
     */
    if (args->ctx == NULL) {
        pamret = pamk5_context_new(args);
        if (pamret != PAM_SUCCESS) {
            pamk5_debug_pam(args, pamret, "creating context failed");
            pamret = PAM_AUTHTOK_ERR;
            goto done;
        }
        pamret = pam_set_data(pamh, "pam_krb5", args->ctx,
                              pamk5_context_destroy);
        if (pamret != PAM_SUCCESS) {
            pamk5_err_pam(args, pamret, "cannot set context data");
            pamret = PAM_AUTHTOK_ERR;
            goto done;
        }
    }
    ctx = args->ctx;

    /* Tell the user what's going on if we're handling an expiration. */
    if (ctx->expired && ctx->creds == NULL)
        pamk5_conv(args, "Password expired.  You must change it now.",
                   PAM_TEXT_INFO, NULL);

    /*
     * Do the password change.  This may only get tickets if we're doing the
     * preliminary check phase.
     */
    pamret = pamk5_password_change(args, !(flags & PAM_UPDATE_AUTHTOK));
    if (!(flags & PAM_UPDATE_AUTHTOK))
        goto done;

    /*
     * If we were handling a password change for an expired password, now
     * try to get a ticket cache with the new password.
     */
    if (pamret == PAM_SUCCESS && ctx->expired) {
        krb5_creds *creds = NULL;

        pamk5_debug(args, "obtaining credentials with new password");
        args->force_first_pass = 1;
        pamret = pamk5_password_auth(args, NULL, &creds);
        if (pamret != PAM_SUCCESS)
            goto done;
        pamret = pamk5_cache_init_random(args, creds);
    }

done:
    if (pamret != PAM_SUCCESS) {
        if (pamret == PAM_SERVICE_ERR || pamret == PAM_AUTH_ERR)
            pamret = PAM_AUTHTOK_ERR;
        if (pamret == PAM_AUTHINFO_UNAVAIL)
            pamret = PAM_AUTHTOK_ERR;
    }
    EXIT(args, pamret);
    if (pass != NULL) {
        memset(pass, 0, strlen(pass));
        free(pass);
    }
    pamk5_args_free(args);
    return pamret;
}