]>
Commit | Line | Data |
---|---|---|
a5c9cd31 | 1 | /* |
2 | * Author: Damien Miller | |
3 | * Copyright (c) 1999 Damien Miller <djm@mindrot.org> | |
4 | * All rights reserved | |
5 | * Created: Thursday December 30 1999 | |
6 | * PAM authentication and session management code. | |
7 | */ | |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #ifdef USE_PAM | |
12 | #include "ssh.h" | |
13 | #include "xmalloc.h" | |
14 | #include "servconf.h" | |
15 | ||
16 | RCSID("$Id$"); | |
17 | ||
5daf7064 | 18 | #define NEW_AUTHTOK_MSG \ |
19 | "Warning: You password has expired, please change it now" | |
20 | ||
a5c9cd31 | 21 | /* Callbacks */ |
22 | static int pamconv(int num_msg, const struct pam_message **msg, | |
23 | struct pam_response **resp, void *appdata_ptr); | |
24 | void pam_cleanup_proc(void *context); | |
5daf7064 | 25 | void pam_msg_cat(const char *msg); |
a5c9cd31 | 26 | |
27 | /* module-local variables */ | |
28 | static struct pam_conv conv = { | |
29 | pamconv, | |
30 | NULL | |
31 | }; | |
32 | static struct pam_handle_t *pamh = NULL; | |
33 | static const char *pampasswd = NULL; | |
5daf7064 | 34 | static char *pam_msg = NULL; |
a5c9cd31 | 35 | |
36 | /* PAM conversation function. This is really a kludge to get the password */ | |
37 | /* into PAM and to pick up any messages generated by PAM into pamconv_msg */ | |
38 | static int pamconv(int num_msg, const struct pam_message **msg, | |
39 | struct pam_response **resp, void *appdata_ptr) | |
40 | { | |
41 | struct pam_response *reply; | |
42 | int count; | |
a5c9cd31 | 43 | |
44 | /* PAM will free this later */ | |
45 | reply = malloc(num_msg * sizeof(*reply)); | |
46 | if (reply == NULL) | |
47 | return PAM_CONV_ERR; | |
48 | ||
49 | for(count = 0; count < num_msg; count++) { | |
50 | switch (msg[count]->msg_style) { | |
51 | case PAM_PROMPT_ECHO_OFF: | |
52 | if (pampasswd == NULL) { | |
53 | free(reply); | |
54 | return PAM_CONV_ERR; | |
55 | } | |
56 | reply[count].resp_retcode = PAM_SUCCESS; | |
57 | reply[count].resp = xstrdup(pampasswd); | |
58 | break; | |
a5c9cd31 | 59 | case PAM_TEXT_INFO: |
60 | reply[count].resp_retcode = PAM_SUCCESS; | |
61 | reply[count].resp = xstrdup(""); | |
62 | ||
5daf7064 | 63 | if (msg[count]->msg != NULL) |
64 | pam_msg_cat(msg[count]->msg); | |
a5c9cd31 | 65 | |
a5c9cd31 | 66 | break; |
a5c9cd31 | 67 | default: |
68 | free(reply); | |
69 | return PAM_CONV_ERR; | |
70 | } | |
71 | } | |
72 | ||
73 | *resp = reply; | |
74 | ||
75 | return PAM_SUCCESS; | |
76 | } | |
77 | ||
78 | /* Called at exit to cleanly shutdown PAM */ | |
79 | void pam_cleanup_proc(void *context) | |
80 | { | |
81 | int pam_retval; | |
82 | ||
83 | if (pamh != NULL) | |
84 | { | |
85 | pam_retval = pam_close_session((pam_handle_t *)pamh, 0); | |
86 | if (pam_retval != PAM_SUCCESS) { | |
87 | log("Cannot close PAM session: %.200s", | |
5daf7064 | 88 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
a5c9cd31 | 89 | } |
90 | ||
91 | pam_retval = pam_setcred((pam_handle_t *)pamh, PAM_DELETE_CRED); | |
92 | if (pam_retval != PAM_SUCCESS) { | |
93 | log("Cannot delete credentials: %.200s", | |
5daf7064 | 94 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
a5c9cd31 | 95 | } |
96 | ||
97 | pam_retval = pam_end((pam_handle_t *)pamh, pam_retval); | |
98 | if (pam_retval != PAM_SUCCESS) { | |
99 | log("Cannot release PAM authentication: %.200s", | |
5daf7064 | 100 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
a5c9cd31 | 101 | } |
102 | } | |
103 | } | |
104 | ||
105 | /* Attempt password authentation using PAM */ | |
106 | int auth_pam_password(struct passwd *pw, const char *password) | |
107 | { | |
108 | extern ServerOptions options; | |
109 | int pam_retval; | |
110 | ||
111 | /* deny if no user. */ | |
112 | if (pw == NULL) | |
113 | return 0; | |
114 | if (pw->pw_uid == 0 && options.permit_root_login == 2) | |
115 | return 0; | |
116 | if (*password == '\0' && options.permit_empty_passwd == 0) | |
117 | return 0; | |
118 | ||
119 | pampasswd = password; | |
120 | ||
121 | pam_retval = pam_authenticate((pam_handle_t *)pamh, 0); | |
122 | if (pam_retval == PAM_SUCCESS) { | |
5daf7064 | 123 | debug("PAM Password authentication accepted for user \"%.100s\"", |
124 | pw->pw_name); | |
a5c9cd31 | 125 | return 1; |
126 | } else { | |
127 | debug("PAM Password authentication for \"%.100s\" failed: %s", | |
128 | pw->pw_name, PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | |
129 | return 0; | |
130 | } | |
131 | } | |
132 | ||
133 | /* Do account management using PAM */ | |
134 | int do_pam_account(char *username, char *remote_user) | |
135 | { | |
136 | int pam_retval; | |
5daf7064 | 137 | |
a5c9cd31 | 138 | debug("PAM setting rhost to \"%.200s\"", get_canonical_hostname()); |
139 | pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RHOST, | |
140 | get_canonical_hostname()); | |
141 | if (pam_retval != PAM_SUCCESS) { | |
5daf7064 | 142 | fatal("PAM set rhost failed: %.200s", |
143 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | |
a5c9cd31 | 144 | } |
145 | ||
146 | if (remote_user != NULL) { | |
147 | debug("PAM setting ruser to \"%.200s\"", remote_user); | |
148 | pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RUSER, remote_user); | |
149 | if (pam_retval != PAM_SUCCESS) { | |
5daf7064 | 150 | fatal("PAM set ruser failed: %.200s", |
151 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | |
a5c9cd31 | 152 | } |
153 | } | |
154 | ||
155 | pam_retval = pam_acct_mgmt((pam_handle_t *)pamh, 0); | |
5daf7064 | 156 | switch (pam_retval) { |
157 | case PAM_SUCCESS: | |
158 | /* This is what we want */ | |
159 | break; | |
160 | case PAM_NEW_AUTHTOK_REQD: | |
161 | pam_msg_cat(NEW_AUTHTOK_MSG); | |
162 | break; | |
163 | default: | |
164 | log("PAM rejected by account configuration: %.200s", | |
165 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | |
166 | return(0); | |
a5c9cd31 | 167 | } |
168 | ||
169 | return(1); | |
170 | } | |
171 | ||
172 | /* Do PAM-specific session initialisation */ | |
fd094f49 | 173 | void do_pam_session(char *username, const char *ttyname) |
a5c9cd31 | 174 | { |
175 | int pam_retval; | |
176 | ||
177 | if (ttyname != NULL) { | |
178 | debug("PAM setting tty to \"%.200s\"", ttyname); | |
179 | pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_TTY, ttyname); | |
5daf7064 | 180 | if (pam_retval != PAM_SUCCESS) { |
181 | fatal("PAM set tty failed: %.200s", | |
182 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | |
183 | } | |
a5c9cd31 | 184 | } |
185 | ||
186 | pam_retval = pam_open_session((pam_handle_t *)pamh, 0); | |
5daf7064 | 187 | if (pam_retval != PAM_SUCCESS) { |
188 | fatal("PAM session setup failed: %.200s", | |
189 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | |
190 | } | |
a5c9cd31 | 191 | } |
192 | ||
193 | /* Set PAM credentials */ | |
194 | void do_pam_setcred() | |
195 | { | |
196 | int pam_retval; | |
197 | ||
198 | debug("PAM establishing creds"); | |
199 | pam_retval = pam_setcred((pam_handle_t *)pamh, PAM_ESTABLISH_CRED); | |
5daf7064 | 200 | if (pam_retval != PAM_SUCCESS) { |
201 | fatal("PAM setcred failed: %.200s", | |
202 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | |
203 | } | |
a5c9cd31 | 204 | } |
205 | ||
206 | /* Cleanly shutdown PAM */ | |
207 | void finish_pam(void) | |
208 | { | |
209 | pam_cleanup_proc(NULL); | |
210 | fatal_remove_cleanup(&pam_cleanup_proc, NULL); | |
211 | } | |
212 | ||
213 | /* Start PAM authentication for specified account */ | |
214 | void start_pam(struct passwd *pw) | |
215 | { | |
216 | int pam_retval; | |
217 | ||
218 | debug("Starting up PAM with username \"%.200s\"", pw->pw_name); | |
219 | ||
4e577b89 | 220 | pam_retval = pam_start(SSHD_PAM_SERVICE, pw->pw_name, &conv, |
221 | (pam_handle_t**)&pamh); | |
a5c9cd31 | 222 | |
5daf7064 | 223 | if (pam_retval != PAM_SUCCESS) { |
224 | fatal("PAM initialisation failed: %.200s", | |
225 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | |
226 | } | |
227 | ||
a5c9cd31 | 228 | fatal_add_cleanup(&pam_cleanup_proc, NULL); |
229 | } | |
230 | ||
231 | /* Return list of PAM enviornment strings */ | |
232 | char **fetch_pam_environment(void) | |
233 | { | |
2b763e31 | 234 | #ifdef HAVE_PAM_GETENVLIST |
a5c9cd31 | 235 | return(pam_getenvlist((pam_handle_t *)pamh)); |
2b763e31 | 236 | #else /* HAVE_PAM_GETENVLIST */ |
237 | return(NULL); | |
238 | #endif /* HAVE_PAM_GETENVLIST */ | |
a5c9cd31 | 239 | } |
240 | ||
241 | /* Print any messages that have been generated during authentication */ | |
242 | /* or account checking to stderr */ | |
243 | void print_pam_messages(void) | |
244 | { | |
5daf7064 | 245 | if (pam_msg != NULL) |
246 | fprintf(stderr, pam_msg); | |
247 | } | |
248 | ||
249 | /* Append a message to the PAM message buffer */ | |
250 | void pam_msg_cat(const char *msg) | |
251 | { | |
252 | char *p; | |
253 | size_t new_msg_len; | |
254 | size_t pam_msg_len; | |
255 | ||
256 | new_msg_len = strlen(msg); | |
257 | ||
258 | if (pam_msg) { | |
259 | pam_msg_len = strlen(pam_msg); | |
260 | pam_msg = xrealloc(pam_msg, new_msg_len + pam_msg_len + 2); | |
261 | p = pam_msg + pam_msg_len; | |
262 | } else { | |
263 | pam_msg = p = xmalloc(new_msg_len + 2); | |
264 | } | |
265 | ||
266 | memcpy(p, msg, new_msg_len); | |
267 | p[new_msg_len] = '\n'; | |
268 | p[new_msg_len + 1] = '\0'; | |
a5c9cd31 | 269 | } |
270 | ||
271 | #endif /* USE_PAM */ |