Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libpam / modules / pam_krb5 / pam_krb5_pass.c
1 /*
2  * pam_krb5_pass.c
3  *
4  * PAM password management functions for pam_krb5
5  *
6  * $FreeBSD: src/lib/libpam/modules/pam_krb5/pam_krb5_pass.c,v 1.1.2.1 2001/06/07 09:37:07 markm Exp $
7  */
8
9 static const char rcsid[] = "$Id: pam_krb5_pass.c,v 1.3 1999/01/19 23:43:11 fcusack Exp $";
10
11 #include <errno.h>
12 #include <stdio.h>      /* sprintf */
13 #include <stdlib.h>     /* malloc */
14 #include <syslog.h>     /* syslog */
15 #include <security/pam_appl.h>
16 #include <security/pam_modules.h>
17 #include <krb5.h>
18 #include <com_err.h>
19 #include "pam_krb5.h"
20
21 /* A useful logging macro */
22 #define DLOG(error_func, error_msg) \
23 if (debug) \
24     syslog(LOG_DEBUG, "pam_krb5: pam_sm_chauthtok(%s %s): %s: %s", \
25            service, name, error_func, error_msg)
26
27 /* Change a user's password */
28 int
29 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
30 {
31     krb5_error_code     krbret;
32     krb5_context        pam_context;
33     krb5_creds          creds;
34     krb5_principal      princ;
35     krb5_get_init_creds_opt opts;
36
37     int         result_code;
38     krb5_data   result_code_string, result_string;
39
40     int         pamret, i;
41     char        *name, *service = NULL, *pass = NULL, *pass2;
42     char        *princ_name = NULL;
43     char        *prompt = NULL;
44
45     int debug = 0;
46     int try_first_pass = 0, use_first_pass = 0;
47
48     if (!(flags & PAM_UPDATE_AUTHTOK))
49         return PAM_AUTHTOK_ERR;
50
51     for (i = 0; i < argc; i++) {
52         if (strcmp(argv[i], "debug") == 0)
53             debug = 1;
54         else if (strcmp(argv[i], "try_first_pass") == 0)
55             try_first_pass = 1;
56         else if (strcmp(argv[i], "use_first_pass") == 0)
57             use_first_pass = 1;
58     }
59
60     /* Get username */
61     if ((pam_get_item(pamh, PAM_USER, (const void **) &name)) != 0) {
62         return PAM_SERVICE_ERR;
63     }
64
65     /* Get service name */
66     (void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
67     if (!service)
68         service = "unknown";
69
70     DLOG("entry", "");
71
72     if ((krbret = krb5_init_context(&pam_context)) != 0) {
73         DLOG("krb5_init_context()", error_message(krbret));
74         return PAM_SERVICE_ERR;
75     }
76
77     if ((krbret = krb5_init_context(&pam_context)) != 0) {
78         DLOG("krb5_init_context()", error_message(krbret));
79         return PAM_SERVICE_ERR;
80     }
81     krb5_get_init_creds_opt_init(&opts);
82     memset(&creds, 0, sizeof(krb5_creds));
83
84     /* Get principal name */
85     if ((krbret = krb5_parse_name(pam_context, name, &princ)) != 0) {
86         DLOG("krb5_parse_name()", error_message(krbret));
87         pamret = PAM_USER_UNKNOWN;
88         goto cleanup3;
89     }
90
91     /* Now convert the principal name into something human readable */
92     if ((krbret = krb5_unparse_name(pam_context, princ, &princ_name)) != 0) {
93         DLOG("krb5_unparse_name()", error_message(krbret));
94         pamret = PAM_SERVICE_ERR;
95         goto cleanup2;
96     }
97
98     /* Get password */
99     prompt = malloc(16 + strlen(princ_name));
100     if (!prompt) {
101         DLOG("malloc()", "failure");
102         pamret = PAM_BUF_ERR;
103         goto cleanup2;
104     }
105     (void) sprintf(prompt, "Password for %s: ", princ_name);
106
107     if (try_first_pass || use_first_pass)
108         (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
109
110 get_pass:
111     if (!pass) {
112         try_first_pass = 0;
113         if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF, 
114           &pass)) != 0) {
115             DLOG("get_user_info()", pam_strerror(pamh, pamret));
116             pamret = PAM_SERVICE_ERR;
117             goto cleanup2;
118         }
119         /* We have to free pass. */
120         if ((pamret = pam_set_item(pamh, PAM_AUTHTOK, pass)) != 0) {
121             DLOG("pam_set_item()", pam_strerror(pamh, pamret));
122             free(pass);
123             pamret = PAM_SERVICE_ERR;
124             goto cleanup2;
125         }
126         free(pass);
127         /* Now we get it back from the library. */
128         (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
129     }
130
131     if ((krbret = krb5_get_init_creds_password(pam_context, &creds, princ, 
132       pass, pam_prompter, pamh, 0, "kadmin/changepw", &opts)) != 0) {
133         DLOG("krb5_get_init_creds_password()", error_message(krbret));
134         if (try_first_pass && krbret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
135             pass = NULL;
136             goto get_pass;
137         }
138         pamret = PAM_AUTH_ERR;
139         goto cleanup2;
140     }
141
142     /* Now get the new password */
143     free(prompt);
144     prompt = "Enter new password: ";
145     if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF, &pass)) 
146       != 0) {
147         DLOG("get_user_info()", pam_strerror(pamh, pamret));
148         prompt = NULL;
149         pamret = PAM_SERVICE_ERR;
150         goto cleanup;
151     }
152     prompt = "Enter it again: ";
153     if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF, &pass2)) 
154       != 0) {
155         DLOG("get_user_info()", pam_strerror(pamh, pamret));
156         prompt = NULL;
157         pamret = PAM_SERVICE_ERR;
158         goto cleanup;
159     }
160     prompt = NULL;
161
162     if (strcmp(pass, pass2) != 0) {
163         DLOG("strcmp()", "passwords not equal");
164         pamret = PAM_AUTHTOK_ERR;
165         goto cleanup;
166     }
167
168     /* Change it */
169     if ((krbret = krb5_change_password(pam_context, &creds, pass,
170       &result_code, &result_code_string, &result_string)) != 0) {
171         DLOG("krb5_change_password()", error_message(krbret));
172         pamret = PAM_AUTHTOK_ERR;
173         goto cleanup;
174     }
175     if (result_code) {
176         DLOG("krb5_change_password() (result_code)", "");
177         pamret = PAM_AUTHTOK_ERR;
178         goto cleanup;
179     }
180
181     if (result_string.data)
182         free(result_string.data);
183     if (result_code_string.data)
184         free(result_code_string.data);
185
186 cleanup:
187     krb5_free_cred_contents(pam_context, &creds);
188 cleanup2:
189     krb5_free_principal(pam_context, princ);
190 cleanup3:
191     if (prompt)
192         free(prompt);
193     if (princ_name)
194         free(princ_name);
195
196     krb5_free_context(pam_context);
197     DLOG("exit", pamret ? "failure" : "success");
198     return pamret;
199 }
200