auth-pam.c (92879) | auth-pam.c (98941) |
---|---|
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. --- 11 unchanged lines hidden (view full) --- 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 | 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. --- 11 unchanged lines hidden (view full) --- 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 <security/pam_appl.h> | |
29#include "ssh.h" 30#include "xmalloc.h" 31#include "log.h" | 28#include "ssh.h" 29#include "xmalloc.h" 30#include "log.h" |
31#include "auth.h" 32#include "auth-pam.h" |
|
32#include "servconf.h" | 33#include "servconf.h" |
33#include "readpass.h" | |
34#include "canohost.h" | 34#include "canohost.h" |
35#include "readpass.h" |
|
35 | 36 |
36RCSID("$FreeBSD: head/crypto/openssh/auth-pam.c 92879 2002-03-21 12:55:21Z des $"); | 37extern char *__progname; |
37 | 38 |
39RCSID("$Id: auth-pam.c,v 1.46 2002/05/08 02:27:56 djm Exp $"); 40 |
|
38#define NEW_AUTHTOK_MSG \ 39 "Warning: Your password has expired, please change it now" 40 | 41#define NEW_AUTHTOK_MSG \ 42 "Warning: Your password has expired, please change it now" 43 |
41#define SSHD_PAM_SERVICE "sshd" 42#define PAM_STRERROR(a, b) pam_strerror((a), (b)) 43 44/* Callbacks */ | |
45static int do_pam_conversation(int num_msg, const struct pam_message **msg, | 44static int do_pam_conversation(int num_msg, const struct pam_message **msg, |
46 struct pam_response **resp, void *appdata_ptr); 47void do_pam_cleanup_proc(void *context); 48void pam_msg_cat(const char *msg); | 45 struct pam_response **resp, void *appdata_ptr); |
49 50/* module-local variables */ 51static struct pam_conv conv = { 52 do_pam_conversation, 53 NULL 54}; | 46 47/* module-local variables */ 48static struct pam_conv conv = { 49 do_pam_conversation, 50 NULL 51}; |
55static pam_handle_t *pamh = NULL; 56static const char *pampasswd = NULL; 57static char *pam_msg = NULL; 58extern ServerOptions options; | 52static char *__pam_msg = NULL; 53static pam_handle_t *__pamh = NULL; 54static const char *__pampasswd = NULL; |
59 60/* states for do_pam_conversation() */ | 55 56/* states for do_pam_conversation() */ |
61typedef enum { INITIAL_LOGIN, OTHER } pamstates; 62static pamstates pamstate = INITIAL_LOGIN; | 57enum { INITIAL_LOGIN, OTHER } pamstate = INITIAL_LOGIN; |
63/* remember whether pam_acct_mgmt() returned PAM_NEWAUTHTOK_REQD */ 64static int password_change_required = 0; 65/* remember whether the last pam_authenticate() succeeded or not */ 66static int was_authenticated = 0; 67 68/* Remember what has been initialised */ 69static int session_opened = 0; 70static int creds_set = 0; 71 | 58/* remember whether pam_acct_mgmt() returned PAM_NEWAUTHTOK_REQD */ 59static int password_change_required = 0; 60/* remember whether the last pam_authenticate() succeeded or not */ 61static int was_authenticated = 0; 62 63/* Remember what has been initialised */ 64static int session_opened = 0; 65static int creds_set = 0; 66 |
72/* 73 * accessor which allows us to switch conversation structs according to 74 * the authentication method being used 75 */ | 67/* accessor which allows us to switch conversation structs according to 68 * the authentication method being used */ |
76void do_pam_set_conv(struct pam_conv *conv) 77{ | 69void do_pam_set_conv(struct pam_conv *conv) 70{ |
78 pam_set_item(pamh, PAM_CONV, conv); | 71 pam_set_item(__pamh, PAM_CONV, conv); |
79} 80 | 72} 73 |
74/* start an authentication run */ 75int do_pam_authenticate(int flags) 76{ 77 int retval = pam_authenticate(__pamh, flags); 78 was_authenticated = (retval == PAM_SUCCESS); 79 return retval; 80} 81 |
|
81/* 82 * PAM conversation function. 83 * There are two states this can run in. 84 * 85 * INITIAL_LOGIN mode simply feeds the password from the client into 86 * PAM in response to PAM_PROMPT_ECHO_OFF, and collects output | 82/* 83 * PAM conversation function. 84 * There are two states this can run in. 85 * 86 * INITIAL_LOGIN mode simply feeds the password from the client into 87 * PAM in response to PAM_PROMPT_ECHO_OFF, and collects output |
87 * messages with pam_msg_cat(). This is used during initial | 88 * messages with into __pam_msg. This is used during initial |
88 * authentication to bypass the normal PAM password prompt. 89 * | 89 * authentication to bypass the normal PAM password prompt. 90 * |
90 * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase(prompt, 1) | 91 * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase() |
91 * and outputs messages to stderr. This mode is used if pam_chauthtok() 92 * is called to update expired passwords. 93 */ 94static int do_pam_conversation(int num_msg, const struct pam_message **msg, 95 struct pam_response **resp, void *appdata_ptr) 96{ 97 struct pam_response *reply; 98 int count; 99 char buf[1024]; 100 101 /* PAM will free this later */ 102 reply = malloc(num_msg * sizeof(*reply)); 103 if (reply == NULL) | 92 * and outputs messages to stderr. This mode is used if pam_chauthtok() 93 * is called to update expired passwords. 94 */ 95static int do_pam_conversation(int num_msg, const struct pam_message **msg, 96 struct pam_response **resp, void *appdata_ptr) 97{ 98 struct pam_response *reply; 99 int count; 100 char buf[1024]; 101 102 /* PAM will free this later */ 103 reply = malloc(num_msg * sizeof(*reply)); 104 if (reply == NULL) |
104 return PAM_CONV_ERR; | 105 return PAM_CONV_ERR; |
105 106 for (count = 0; count < num_msg; count++) { | 106 107 for (count = 0; count < num_msg; count++) { |
107 switch ((*msg)[count].msg_style) { | 108 if (pamstate == INITIAL_LOGIN) { 109 /* 110 * We can't use stdio yet, queue messages for 111 * printing later 112 */ 113 switch(PAM_MSG_MEMBER(msg, count, msg_style)) { |
108 case PAM_PROMPT_ECHO_ON: | 114 case PAM_PROMPT_ECHO_ON: |
109 if (pamstate == INITIAL_LOGIN) { | 115 free(reply); 116 return PAM_CONV_ERR; 117 case PAM_PROMPT_ECHO_OFF: 118 if (__pampasswd == NULL) { |
110 free(reply); 111 return PAM_CONV_ERR; | 119 free(reply); 120 return PAM_CONV_ERR; |
112 } else { 113 fputs((*msg)[count].msg, stderr); 114 fgets(buf, sizeof(buf), stdin); 115 reply[count].resp = xstrdup(buf); 116 reply[count].resp_retcode = PAM_SUCCESS; 117 break; | |
118 } | 121 } |
119 case PAM_PROMPT_ECHO_OFF: 120 if (pamstate == INITIAL_LOGIN) { 121 if (pampasswd == NULL) { 122 free(reply); 123 return PAM_CONV_ERR; 124 } 125 reply[count].resp = xstrdup(pampasswd); 126 } else { 127 reply[count].resp = 128 xstrdup(read_passphrase((*msg)[count].msg, 1)); 129 } | 122 reply[count].resp = xstrdup(__pampasswd); |
130 reply[count].resp_retcode = PAM_SUCCESS; 131 break; 132 case PAM_ERROR_MSG: 133 case PAM_TEXT_INFO: 134 if ((*msg)[count].msg != NULL) { | 123 reply[count].resp_retcode = PAM_SUCCESS; 124 break; 125 case PAM_ERROR_MSG: 126 case PAM_TEXT_INFO: 127 if ((*msg)[count].msg != NULL) { |
135 if (pamstate == INITIAL_LOGIN) 136 pam_msg_cat((*msg)[count].msg); 137 else { 138 fputs((*msg)[count].msg, stderr); 139 fputs("\n", stderr); 140 } | 128 message_cat(&__pam_msg, 129 PAM_MSG_MEMBER(msg, count, msg)); |
141 } 142 reply[count].resp = xstrdup(""); 143 reply[count].resp_retcode = PAM_SUCCESS; 144 break; 145 default: 146 free(reply); 147 return PAM_CONV_ERR; | 130 } 131 reply[count].resp = xstrdup(""); 132 reply[count].resp_retcode = PAM_SUCCESS; 133 break; 134 default: 135 free(reply); 136 return PAM_CONV_ERR; |
137 } 138 } else { 139 /* 140 * stdio is connected, so interact directly 141 */ 142 switch(PAM_MSG_MEMBER(msg, count, msg_style)) { 143 case PAM_PROMPT_ECHO_ON: 144 fputs(PAM_MSG_MEMBER(msg, count, msg), stderr); 145 fgets(buf, sizeof(buf), stdin); 146 reply[count].resp = xstrdup(buf); 147 reply[count].resp_retcode = PAM_SUCCESS; 148 break; 149 case PAM_PROMPT_ECHO_OFF: 150 reply[count].resp = 151 read_passphrase(PAM_MSG_MEMBER(msg, count, 152 msg), RP_ALLOW_STDIN); 153 reply[count].resp_retcode = PAM_SUCCESS; 154 break; 155 case PAM_ERROR_MSG: 156 case PAM_TEXT_INFO: 157 if ((*msg)[count].msg != NULL) 158 fprintf(stderr, "%s\n", 159 PAM_MSG_MEMBER(msg, count, msg)); 160 reply[count].resp = xstrdup(""); 161 reply[count].resp_retcode = PAM_SUCCESS; 162 break; 163 default: 164 free(reply); 165 return PAM_CONV_ERR; 166 } |
|
148 } 149 } 150 151 *resp = reply; 152 153 return PAM_SUCCESS; 154} 155 156/* Called at exit to cleanly shutdown PAM */ 157void do_pam_cleanup_proc(void *context) 158{ | 167 } 168 } 169 170 *resp = reply; 171 172 return PAM_SUCCESS; 173} 174 175/* Called at exit to cleanly shutdown PAM */ 176void do_pam_cleanup_proc(void *context) 177{ |
159 int pam_retval; | 178 int pam_retval = PAM_SUCCESS; |
160 | 179 |
161 if (pamh != NULL && session_opened) { 162 pam_retval = pam_close_session(pamh, 0); 163 if (pam_retval != PAM_SUCCESS) { 164 log("Cannot close PAM session[%d]: %.200s", 165 pam_retval, PAM_STRERROR(pamh, pam_retval)); 166 } | 180 if (__pamh && session_opened) { 181 pam_retval = pam_close_session(__pamh, 0); 182 if (pam_retval != PAM_SUCCESS) 183 log("Cannot close PAM session[%d]: %.200s", 184 pam_retval, PAM_STRERROR(__pamh, pam_retval)); |
167 } 168 | 185 } 186 |
169 if (pamh != NULL && creds_set) { 170 pam_retval = pam_setcred(pamh, PAM_DELETE_CRED); 171 if (pam_retval != PAM_SUCCESS) { | 187 if (__pamh && creds_set) { 188 pam_retval = pam_setcred(__pamh, PAM_DELETE_CRED); 189 if (pam_retval != PAM_SUCCESS) |
172 debug("Cannot delete credentials[%d]: %.200s", | 190 debug("Cannot delete credentials[%d]: %.200s", |
173 pam_retval, PAM_STRERROR(pamh, pam_retval)); 174 } | 191 pam_retval, PAM_STRERROR(__pamh, pam_retval)); |
175 } 176 | 192 } 193 |
177 if (pamh != NULL) { 178 pam_retval = pam_end(pamh, pam_retval); 179 if (pam_retval != PAM_SUCCESS) { 180 log("Cannot release PAM authentication[%d]: %.200s", 181 pam_retval, PAM_STRERROR(pamh, pam_retval)); 182 } | 194 if (__pamh) { 195 pam_retval = pam_end(__pamh, pam_retval); 196 if (pam_retval != PAM_SUCCESS) 197 log("Cannot release PAM authentication[%d]: %.200s", 198 pam_retval, PAM_STRERROR(__pamh, pam_retval)); |
183 } 184} 185 186/* Attempt password authentation using PAM */ 187int auth_pam_password(Authctxt *authctxt, const char *password) 188{ | 199 } 200} 201 202/* Attempt password authentation using PAM */ 203int auth_pam_password(Authctxt *authctxt, const char *password) 204{ |
189 struct passwd *pw = authctxt->pw; | 205 extern ServerOptions options; |
190 int pam_retval; | 206 int pam_retval; |
207 struct passwd *pw = authctxt->pw; |
|
191 192 do_pam_set_conv(&conv); 193 194 /* deny if no user. */ 195 if (pw == NULL) 196 return 0; | 208 209 do_pam_set_conv(&conv); 210 211 /* deny if no user. */ 212 if (pw == NULL) 213 return 0; |
197 if (pw->pw_uid == 0 && options.permit_root_login == 2) | 214 if (pw->pw_uid == 0 && options.permit_root_login == PERMIT_NO_PASSWD) |
198 return 0; 199 if (*password == '\0' && options.permit_empty_passwd == 0) 200 return 0; 201 | 215 return 0; 216 if (*password == '\0' && options.permit_empty_passwd == 0) 217 return 0; 218 |
202 pampasswd = password; 203 | 219 __pampasswd = password; 220 |
204 pamstate = INITIAL_LOGIN; | 221 pamstate = INITIAL_LOGIN; |
205 pam_retval = pam_authenticate(pamh, 0); 206 was_authenticated = (pam_retval == PAM_SUCCESS); | 222 pam_retval = do_pam_authenticate( 223 options.permit_empty_passwd == 0 ? PAM_DISALLOW_NULL_AUTHTOK : 0); |
207 if (pam_retval == PAM_SUCCESS) { | 224 if (pam_retval == PAM_SUCCESS) { |
208 debug("PAM Password authentication accepted for user \"%.100s\"", 209 pw->pw_name); | 225 debug("PAM Password authentication accepted for " 226 "user \"%.100s\"", pw->pw_name); |
210 return 1; 211 } else { | 227 return 1; 228 } else { |
212 debug("PAM Password authentication for \"%.100s\" failed[%d]: %s", 213 pw->pw_name, pam_retval, PAM_STRERROR(pamh, pam_retval)); | 229 debug("PAM Password authentication for \"%.100s\" " 230 "failed[%d]: %s", pw->pw_name, pam_retval, 231 PAM_STRERROR(__pamh, pam_retval)); |
214 return 0; 215 } 216} 217 218/* Do account management using PAM */ 219int do_pam_account(char *username, char *remote_user) 220{ 221 int pam_retval; 222 223 do_pam_set_conv(&conv); | 232 return 0; 233 } 234} 235 236/* Do account management using PAM */ 237int do_pam_account(char *username, char *remote_user) 238{ 239 int pam_retval; 240 241 do_pam_set_conv(&conv); |
224 225 debug("PAM setting rhost to \"%.200s\"", 226 get_canonical_hostname(options.verify_reverse_mapping)); 227 pam_retval = pam_set_item(pamh, PAM_RHOST, 228 get_canonical_hostname(options.verify_reverse_mapping)); 229 if (pam_retval != PAM_SUCCESS) { 230 fatal("PAM set rhost failed[%d]: %.200s", 231 pam_retval, PAM_STRERROR(pamh, pam_retval)); 232 } | |
233 | 242 |
234 if (remote_user != NULL) { | 243 if (remote_user) { |
235 debug("PAM setting ruser to \"%.200s\"", remote_user); | 244 debug("PAM setting ruser to \"%.200s\"", remote_user); |
236 pam_retval = pam_set_item(pamh, PAM_RUSER, remote_user); 237 if (pam_retval != PAM_SUCCESS) { 238 fatal("PAM set ruser failed[%d]: %.200s", 239 pam_retval, PAM_STRERROR(pamh, pam_retval)); 240 } | 245 pam_retval = pam_set_item(__pamh, PAM_RUSER, remote_user); 246 if (pam_retval != PAM_SUCCESS) 247 fatal("PAM set ruser failed[%d]: %.200s", pam_retval, 248 PAM_STRERROR(__pamh, pam_retval)); |
241 } 242 | 249 } 250 |
243 pam_retval = pam_acct_mgmt(pamh, 0); | 251 pam_retval = pam_acct_mgmt(__pamh, 0); 252 debug2("pam_acct_mgmt() = %d", pam_retval); |
244 switch (pam_retval) { 245 case PAM_SUCCESS: 246 /* This is what we want */ 247 break; | 253 switch (pam_retval) { 254 case PAM_SUCCESS: 255 /* This is what we want */ 256 break; |
257#if 0 |
|
248 case PAM_NEW_AUTHTOK_REQD: | 258 case PAM_NEW_AUTHTOK_REQD: |
249 pam_msg_cat(NEW_AUTHTOK_MSG); | 259 message_cat(&__pam_msg, NEW_AUTHTOK_MSG); |
250 /* flag that password change is necessary */ 251 password_change_required = 1; 252 break; | 260 /* flag that password change is necessary */ 261 password_change_required = 1; 262 break; |
263#endif |
|
253 default: | 264 default: |
254 log("PAM rejected by account configuration[%d]: %.200s", 255 pam_retval, PAM_STRERROR(pamh, pam_retval)); | 265 log("PAM rejected by account configuration[%d]: " 266 "%.200s", pam_retval, PAM_STRERROR(__pamh, 267 pam_retval)); |
256 return(0); 257 } | 268 return(0); 269 } |
258 | 270 |
259 return(1); 260} 261 262/* Do PAM-specific session initialisation */ 263void do_pam_session(char *username, const char *ttyname) 264{ 265 int pam_retval; 266 267 do_pam_set_conv(&conv); 268 269 if (ttyname != NULL) { 270 debug("PAM setting tty to \"%.200s\"", ttyname); | 271 return(1); 272} 273 274/* Do PAM-specific session initialisation */ 275void do_pam_session(char *username, const char *ttyname) 276{ 277 int pam_retval; 278 279 do_pam_set_conv(&conv); 280 281 if (ttyname != NULL) { 282 debug("PAM setting tty to \"%.200s\"", ttyname); |
271 pam_retval = pam_set_item(pamh, PAM_TTY, ttyname); 272 if (pam_retval != PAM_SUCCESS) { 273 fatal("PAM set tty failed[%d]: %.200s", 274 pam_retval, PAM_STRERROR(pamh, pam_retval)); 275 } | 283 pam_retval = pam_set_item(__pamh, PAM_TTY, ttyname); 284 if (pam_retval != PAM_SUCCESS) 285 fatal("PAM set tty failed[%d]: %.200s", 286 pam_retval, PAM_STRERROR(__pamh, pam_retval)); |
276 } 277 | 287 } 288 |
278 debug("do_pam_session: euid %u, uid %u", geteuid(), getuid()); 279 pam_retval = pam_open_session(pamh, 0); 280 if (pam_retval != PAM_SUCCESS) { 281 fatal("PAM session setup failed[%d]: %.200s", 282 pam_retval, PAM_STRERROR(pamh, pam_retval)); 283 } | 289 pam_retval = pam_open_session(__pamh, 0); 290 if (pam_retval != PAM_SUCCESS) 291 fatal("PAM session setup failed[%d]: %.200s", 292 pam_retval, PAM_STRERROR(__pamh, pam_retval)); |
284 285 session_opened = 1; 286} 287 | 293 294 session_opened = 1; 295} 296 |
288/* Set PAM credentials */ 289void do_pam_setcred(void) | 297/* Set PAM credentials */ 298void do_pam_setcred(int init) |
290{ 291 int pam_retval; 292 | 299{ 300 int pam_retval; 301 |
302 if (__pamh == NULL) 303 return; 304 |
|
293 do_pam_set_conv(&conv); | 305 do_pam_set_conv(&conv); |
294 | 306 |
295 debug("PAM establishing creds"); | 307 debug("PAM establishing creds"); |
296 pam_retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); | 308 pam_retval = pam_setcred(__pamh, 309 init ? PAM_ESTABLISH_CRED : PAM_REINITIALIZE_CRED); |
297 if (pam_retval != PAM_SUCCESS) { 298 if (was_authenticated) 299 fatal("PAM setcred failed[%d]: %.200s", | 310 if (pam_retval != PAM_SUCCESS) { 311 if (was_authenticated) 312 fatal("PAM setcred failed[%d]: %.200s", |
300 pam_retval, PAM_STRERROR(pamh, pam_retval)); | 313 pam_retval, PAM_STRERROR(__pamh, pam_retval)); |
301 else 302 debug("PAM setcred failed[%d]: %.200s", | 314 else 315 debug("PAM setcred failed[%d]: %.200s", |
303 pam_retval, PAM_STRERROR(pamh, pam_retval)); | 316 pam_retval, PAM_STRERROR(__pamh, pam_retval)); |
304 } else 305 creds_set = 1; 306} 307 308/* accessor function for file scope static variable */ | 317 } else 318 creds_set = 1; 319} 320 321/* accessor function for file scope static variable */ |
309int pam_password_change_required(void) | 322int is_pam_password_change_required(void) |
310{ 311 return password_change_required; 312} 313 | 323{ 324 return password_change_required; 325} 326 |
314/* | 327/* |
315 * Have user change authentication token if pam_acct_mgmt() indicated 316 * it was expired. This needs to be called after an interactive 317 * session is established and the user's pty is connected to 318 * stdin/stout/stderr. 319 */ 320void do_pam_chauthtok(void) 321{ 322 int pam_retval; 323 324 do_pam_set_conv(&conv); 325 326 if (password_change_required) { 327 pamstate = OTHER; | 328 * Have user change authentication token if pam_acct_mgmt() indicated 329 * it was expired. This needs to be called after an interactive 330 * session is established and the user's pty is connected to 331 * stdin/stout/stderr. 332 */ 333void do_pam_chauthtok(void) 334{ 335 int pam_retval; 336 337 do_pam_set_conv(&conv); 338 339 if (password_change_required) { 340 pamstate = OTHER; |
328 /* 329 * XXX: should we really loop forever? 330 */ 331 do { 332 pam_retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 333 if (pam_retval != PAM_SUCCESS) { 334 log("PAM pam_chauthtok failed[%d]: %.200s", 335 pam_retval, PAM_STRERROR(pamh, pam_retval)); 336 } 337 } while (pam_retval != PAM_SUCCESS); | 341 pam_retval = pam_chauthtok(__pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 342 if (pam_retval != PAM_SUCCESS) 343 fatal("PAM pam_chauthtok failed[%d]: %.200s", 344 pam_retval, PAM_STRERROR(__pamh, pam_retval)); |
338 } 339} 340 341/* Cleanly shutdown PAM */ 342void finish_pam(void) 343{ 344 do_pam_cleanup_proc(NULL); 345 fatal_remove_cleanup(&do_pam_cleanup_proc, NULL); 346} 347 348/* Start PAM authentication for specified account */ | 345 } 346} 347 348/* Cleanly shutdown PAM */ 349void finish_pam(void) 350{ 351 do_pam_cleanup_proc(NULL); 352 fatal_remove_cleanup(&do_pam_cleanup_proc, NULL); 353} 354 355/* Start PAM authentication for specified account */ |
349void start_pam(struct passwd *pw) | 356void start_pam(const char *user) |
350{ 351 int pam_retval; | 357{ 358 int pam_retval; |
359 extern ServerOptions options; 360 extern u_int utmp_len; 361 const char *rhost; |
|
352 | 362 |
353 debug("Starting up PAM with username \"%.200s\"", pw->pw_name); | 363 debug("Starting up PAM with username \"%.200s\"", user); |
354 | 364 |
355 pam_retval = pam_start(SSHD_PAM_SERVICE, pw->pw_name, &conv, &pamh); | 365 pam_retval = pam_start(SSHD_PAM_SERVICE, user, &conv, &__pamh); |
356 | 366 |
357 if (pam_retval != PAM_SUCCESS) { 358 fatal("PAM initialisation failed[%d]: %.200s", 359 pam_retval, PAM_STRERROR(pamh, pam_retval)); 360 } | 367 if (pam_retval != PAM_SUCCESS) 368 fatal("PAM initialisation failed[%d]: %.200s", 369 pam_retval, PAM_STRERROR(__pamh, pam_retval)); |
361 | 370 |
371 rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping); 372 debug("PAM setting rhost to \"%.200s\"", rhost); 373 374 pam_retval = pam_set_item(__pamh, PAM_RHOST, rhost); 375 if (pam_retval != PAM_SUCCESS) 376 fatal("PAM set rhost failed[%d]: %.200s", pam_retval, 377 PAM_STRERROR(__pamh, pam_retval)); |
|
362#ifdef PAM_TTY_KLUDGE 363 /* 364 * Some PAM modules (e.g. pam_time) require a TTY to operate, | 378#ifdef PAM_TTY_KLUDGE 379 /* 380 * Some PAM modules (e.g. pam_time) require a TTY to operate, |
365 * and will fail in various stupid ways if they don't get one. | 381 * and will fail in various stupid ways if they don't get one. |
366 * sshd doesn't set the tty until too late in the auth process and may 367 * not even need one (for tty-less connections) | 382 * sshd doesn't set the tty until too late in the auth process and may 383 * not even need one (for tty-less connections) |
368 * Kludge: Set a fake PAM_TTY | 384 * Kludge: Set a fake PAM_TTY |
369 */ | 385 */ |
370 pam_retval = pam_set_item(pamh, PAM_TTY, "ssh"); 371 if (pam_retval != PAM_SUCCESS) { 372 fatal("PAM set tty failed[%d]: %.200s", 373 pam_retval, PAM_STRERROR(pamh, pam_retval)); 374 } | 386 pam_retval = pam_set_item(__pamh, PAM_TTY, "NODEVssh"); 387 if (pam_retval != PAM_SUCCESS) 388 fatal("PAM set tty failed[%d]: %.200s", 389 pam_retval, PAM_STRERROR(__pamh, pam_retval)); |
375#endif /* PAM_TTY_KLUDGE */ 376 377 fatal_add_cleanup(&do_pam_cleanup_proc, NULL); 378} 379 380/* Return list of PAM enviornment strings */ 381char **fetch_pam_environment(void) 382{ 383#ifdef HAVE_PAM_GETENVLIST | 390#endif /* PAM_TTY_KLUDGE */ 391 392 fatal_add_cleanup(&do_pam_cleanup_proc, NULL); 393} 394 395/* Return list of PAM enviornment strings */ 396char **fetch_pam_environment(void) 397{ 398#ifdef HAVE_PAM_GETENVLIST |
384 return(pam_getenvlist(pamh)); | 399 return(pam_getenvlist(__pamh)); |
385#else /* HAVE_PAM_GETENVLIST */ 386 return(NULL); 387#endif /* HAVE_PAM_GETENVLIST */ 388} 389 390/* Print any messages that have been generated during authentication */ 391/* or account checking to stderr */ 392void print_pam_messages(void) 393{ | 400#else /* HAVE_PAM_GETENVLIST */ 401 return(NULL); 402#endif /* HAVE_PAM_GETENVLIST */ 403} 404 405/* Print any messages that have been generated during authentication */ 406/* or account checking to stderr */ 407void print_pam_messages(void) 408{ |
394 if (pam_msg != NULL) 395 fputs(pam_msg, stderr); | 409 if (__pam_msg != NULL) 410 fputs(__pam_msg, stderr); |
396} 397 | 411} 412 |
398/* Append a message to the PAM message buffer */ 399void pam_msg_cat(const char *msg) | 413/* Append a message to buffer */ 414void message_cat(char **p, const char *a) |
400{ | 415{ |
401 char *p; 402 size_t new_msg_len; 403 size_t pam_msg_len; 404 405 new_msg_len = strlen(msg); 406 407 if (pam_msg) { 408 pam_msg_len = strlen(pam_msg); 409 pam_msg = xrealloc(pam_msg, new_msg_len + pam_msg_len + 2); 410 p = pam_msg + pam_msg_len; 411 } else { 412 pam_msg = p = xmalloc(new_msg_len + 2); 413 } | 416 char *cp; 417 size_t new_len; |
414 | 418 |
415 memcpy(p, msg, new_msg_len); 416 p[new_msg_len] = '\n'; 417 p[new_msg_len + 1] = '\0'; 418} | 419 new_len = strlen(a); |
419 | 420 |
420struct inverted_pam_userdata { 421 /* 422 * Pipe for telling whether we are doing conversation or sending 423 * authentication results. 424 */ 425 int statefd[2]; 426 int challengefd[2]; 427 int responsefd[2]; | 421 if (*p) { 422 size_t len = strlen(*p); |
428 | 423 |
429 /* Whether we have sent off our challenge */ 430 int state; 431}; | 424 *p = xrealloc(*p, new_len + len + 2); 425 cp = *p + len; 426 } else 427 *p = cp = xmalloc(new_len + 2); |
432 | 428 |
433#define STATE_CONV 1 434#define STATE_AUTH_OK 2 435#define STATE_AUTH_FAIL 3 436 437int 438ssh_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, 439 void *userdata) { 440 int i; 441 FILE *reader; 442 char buf[1024]; 443 struct pam_response *reply = NULL; 444 char state_to_write = STATE_CONV; /* One char to write */ 445 struct inverted_pam_userdata *ud = userdata; 446 char *response = NULL; 447 448 /* The stdio functions are more convenient for the read half */ 449 reader = fdopen(ud->responsefd[0], "rb"); 450 if (reader == NULL) 451 goto protocol_failure; 452 453 reply = malloc(num_msg * sizeof(struct pam_response)); 454 if (reply == NULL) 455 return PAM_CONV_ERR; 456 457 if (write(ud->statefd[1], &state_to_write, 1) != 1) 458 goto protocol_failure; 459 460 /* 461 * Re-package our data and send it off to our better half (the actual SSH 462 * process) 463 */ 464 if (write(ud->challengefd[1], buf, 465 sprintf(buf, "%d\n", num_msg)) == -1) 466 goto protocol_failure; 467 for (i = 0; i < num_msg; i++) { 468 if (write(ud->challengefd[1], buf, 469 sprintf(buf, "%d\n", msg[i]->msg_style)) == -1) 470 goto protocol_failure; 471 if (write(ud->challengefd[1], buf, 472 sprintf(buf, "%d\n", strlen(msg[i]->msg))) == -1) 473 goto protocol_failure; 474 if (write(ud->challengefd[1], msg[i]->msg, 475 strlen(msg[i]->msg)) == -1) 476 goto protocol_failure; 477 } 478 /* 479 * Read back responses. These may not be as nice as we want, as the SSH 480 * protocol isn't exactly a perfect fit with PAM. 481 */ 482 483 for (i = 0; i < num_msg; i++) { 484 char buf[1024]; 485 char *endptr; 486 size_t len; /* Length of the response */ 487 488 switch (msg[i]->msg_style) { 489 case PAM_PROMPT_ECHO_OFF: 490 case PAM_PROMPT_ECHO_ON: 491 if (fgets(buf, sizeof(buf), reader) == NULL) 492 goto protocol_failure; 493 len = (size_t)strtoul(buf, &endptr, 10); 494 /* The length is supposed to stand on a line by itself */ 495 if (endptr == NULL || *endptr != '\n') 496 goto protocol_failure; 497 response = malloc(len+1); 498 if (response == NULL) 499 goto protocol_failure; 500 if (fread(response, len, 1, reader) != 1) 501 goto protocol_failure; 502 response[len] = '\0'; 503 reply[i].resp = response; 504 response = NULL; 505 break; 506 default: 507 reply[i].resp = NULL; 508 break; 509 } 510 } 511 *resp = reply; 512 return PAM_SUCCESS; 513 protocol_failure: 514 free(reply); 515 return PAM_CONV_ERR; | 429 memcpy(cp, a, new_len); 430 cp[new_len] = '\n'; 431 cp[new_len + 1] = '\0'; |
516} 517 | 432} 433 |
518void 519ipam_free_cookie(struct inverted_pam_cookie *cookie) { 520 struct inverted_pam_userdata *ud; 521 int i; 522 523 if (cookie == NULL) 524 return; 525 ud = cookie->userdata; 526 cookie->userdata = NULL; 527 /* Free userdata if allocated */ 528 if (ud) { 529 /* Close any opened file descriptors */ 530 if (ud->statefd[0] != -1) 531 close(ud->statefd[0]); 532 if (ud->statefd[1] != -1) 533 close(ud->statefd[1]); 534 if (ud->challengefd[0] != -1) 535 close(ud->challengefd[0]); 536 if (ud->challengefd[1] != -1) 537 close(ud->challengefd[1]); 538 if (ud->responsefd[0] != -1) 539 close(ud->responsefd[0]); 540 if (ud->responsefd[1] != -1) 541 close(ud->responsefd[1]); 542 free(ud); 543 ud = NULL; 544 } 545 /* Now free the normal cookie */ 546 if (cookie->pid != 0 && cookie->pid != -1) { 547 int status; 548 549 /* XXX Use different signal? */ 550 kill(cookie->pid, SIGKILL); 551 waitpid(cookie->pid, &status, 0); 552 } 553 for (i = 0; i < cookie->num_msg; i++) { 554 if (cookie->resp && cookie->resp[i]) { 555 free(cookie->resp[i]->resp); 556 free(cookie->resp[i]); 557 } 558 if (cookie->msg && cookie->msg[i]) { 559 free((void *)cookie->msg[i]->msg); 560 free(cookie->msg[i]); 561 } 562 } 563 free(cookie->msg); 564 free(cookie->resp); 565 free(cookie); 566} 567 568/* 569 * Do first half of PAM authentication - this comes to the point where 570 * you get a message to send to the user. 571 */ 572struct inverted_pam_cookie * 573ipam_start_auth(const char *service, const char *username) { 574 struct inverted_pam_cookie *cookie; 575 struct inverted_pam_userdata *ud; 576 static struct pam_conv conv = { 577 ssh_conv, 578 NULL 579 }; 580 const char *rhost; 581 582 cookie = malloc(sizeof(*cookie)); 583 if (cookie == NULL) 584 return NULL; 585 cookie->state = 0; 586 /* Set up the cookie so ipam_freecookie can be used on it */ 587 cookie->num_msg = 0; 588 cookie->msg = NULL; 589 cookie->resp = NULL; 590 cookie->pid = -1; 591 592 ud = calloc(sizeof(*ud), 1); 593 if (ud == NULL) { 594 free(cookie); 595 return NULL; 596 } 597 cookie->userdata = ud; 598 ud->statefd[0] = ud->statefd[1] = -1; 599 ud->challengefd[0] = ud->challengefd[1] = -1; 600 ud->responsefd[0] = ud->responsefd[1] = -1; 601 602 if (pipe(ud->statefd) != 0) { 603 ud->statefd[0] = ud->statefd[1] = -1; 604 ipam_free_cookie(cookie); 605 return NULL; 606 } 607 if (pipe(ud->challengefd) != 0) { 608 ud->challengefd[0] = ud->challengefd[1] = -1; 609 ipam_free_cookie(cookie); 610 return NULL; 611 } 612 if (pipe(ud->responsefd) != 0) { 613 ud->responsefd[0] = ud->responsefd[1] = -1; 614 ipam_free_cookie(cookie); 615 return NULL; 616 } 617 rhost = get_canonical_hostname(options.verify_reverse_mapping); 618 cookie->pid = fork(); 619 if (cookie->pid == -1) { 620 ipam_free_cookie(cookie); 621 return NULL; 622 } else if (cookie->pid != 0) { 623 int num_msgs; /* Number of messages from PAM */ 624 char *endptr; 625 char buf[1024]; 626 FILE *reader; 627 size_t num_msg; 628 int i; 629 char state; /* Which state did the connection just enter? */ 630 631 /* We are the parent - wait for a call to the communications 632 function to turn up, or the challenge to be finished */ 633 if (read(ud->statefd[0], &state, 1) != 1) { 634 ipam_free_cookie(cookie); 635 return NULL; 636 } 637 cookie->state = state; 638 switch (state) { 639 case STATE_CONV: 640 /* We are running the conversation function */ 641 /* The stdio functions are more convenient for read */ 642 reader = fdopen(ud->challengefd[0], "r"); 643 if (reader == NULL) { 644 ipam_free_cookie(cookie); 645 return NULL; 646 } 647 if (fgets(buf, 4, reader) == NULL) { 648 fclose(reader); 649 ipam_free_cookie(cookie); 650 return NULL; 651 } 652 num_msg = (size_t)strtoul(buf, &endptr, 10); 653 /* The length is supposed to stand on a line by itself */ 654 if (endptr == NULL || *endptr != '\n') { 655 fclose(reader); 656 ipam_free_cookie(cookie); 657 return NULL; 658 } 659 cookie->msg = 660 malloc(sizeof(struct pam_message *) * num_msg); 661 cookie->resp = 662 malloc(sizeof(struct pam_response *) * num_msg); 663 if (cookie->msg == NULL || cookie->resp == NULL) { 664 fclose(reader); 665 ipam_free_cookie(cookie); 666 return NULL; 667 } 668 for (i = 0; i < num_msg; i++) { 669 cookie->msg[i] = 670 malloc(sizeof(struct pam_message)); 671 cookie->resp[i] = 672 malloc(sizeof(struct pam_response)); 673 if (cookie->msg[i] == NULL || 674 cookie->resp[i] == NULL) { 675 for (;;) { 676 free(cookie->msg[i]); 677 free(cookie->resp[i]); 678 if (i == 0) 679 break; 680 i--; 681 } 682 fclose(reader); 683 ipam_free_cookie(cookie); 684 return NULL; 685 } 686 cookie->msg[i]->msg = NULL; 687 cookie->resp[i]->resp = NULL; 688 cookie->resp[i]->resp_retcode = 0; 689 } 690 /* Set up so the above will be freed on failure */ 691 cookie->num_msg = num_msg; 692 /* 693 * We have a an allocated response and message for 694 * each of the entries in the PAM structure - transfer 695 * the data sent to the conversation function over. 696 */ 697 for (i = 0; i < num_msg; i++) { 698 size_t len; 699 700 if (fgets(buf, sizeof(buf), reader) == NULL) { 701 fclose(reader); 702 ipam_free_cookie(cookie); 703 return NULL; 704 } 705 cookie->msg[i]->msg_style = 706 (size_t)strtoul(buf, &endptr, 10); 707 if (endptr == NULL || *endptr != '\n') { 708 fclose(reader); 709 ipam_free_cookie(cookie); 710 return NULL; 711 } 712 if (fgets(buf, sizeof(buf), reader) == NULL) { 713 fclose(reader); 714 ipam_free_cookie(cookie); 715 return NULL; 716 } 717 len = (size_t)strtoul(buf, &endptr, 10); 718 if (endptr == NULL || *endptr != '\n') { 719 fclose(reader); 720 ipam_free_cookie(cookie); 721 return NULL; 722 } 723 cookie->msg[i]->msg = malloc(len + 1); 724 if (cookie->msg[i]->msg == NULL) { 725 fclose(reader); 726 ipam_free_cookie(cookie); 727 return NULL; 728 } 729 if (fread((char *)cookie->msg[i]->msg, len, 1, reader) != 730 1) { 731 fclose(reader); 732 ipam_free_cookie(cookie); 733 return NULL; 734 } 735 *(char *)&(cookie->msg[i]->msg[len]) = '\0'; 736 } 737 break; 738 case STATE_AUTH_OK: 739 case STATE_AUTH_FAIL: 740 break; 741 default: 742 /* Internal failure, somehow */ 743 fclose(reader); 744 ipam_free_cookie(cookie); 745 return NULL; 746 } 747 return cookie; 748 } else { 749 /* We are the child */ 750 pam_handle_t *pamh=NULL; 751 int retval; 752 char state; 753 754 conv.appdata_ptr = ud; 755 retval = pam_start(service, username, &conv, &pamh); 756 fprintf(stderr, "pam_start returned %d\n", retval); 757 if (retval == PAM_SUCCESS) 758 retval = pam_set_item(pamh, PAM_RHOST, rhost); 759 /* Is user really user? */ 760 if (retval == PAM_SUCCESS) 761 retval = pam_authenticate(pamh, 0); 762 /* permitted access? */ 763 if (retval == PAM_SUCCESS) 764 retval = pam_acct_mgmt(pamh, 0); 765 /* This is where we have been authorized or not. */ 766 767 /* Be conservative - flag as auth failure if we can't close */ 768 /* 769 * XXX This is based on example code from Linux-PAM - 770 * but can it really be correct to pam_end if 771 * pam_start failed? 772 */ 773 if (pam_end(pamh, retval) != PAM_SUCCESS) 774 retval = PAM_AUTH_ERR; 775 776 /* Message to parent */ 777 state = retval == PAM_SUCCESS ? STATE_AUTH_OK : STATE_AUTH_FAIL; 778 if (write(ud->statefd[1], &state, 1) != 1) { 779 _exit(1); 780 } 781 /* FDs will be closed, so further communication will stop */ 782 _exit(0); 783 } 784} 785 786/* 787 * Do second half of PAM authentication - cookie should now be filled 788 * in with the response to the challenge. 789 */ 790 791int 792ipam_complete_auth(struct inverted_pam_cookie *cookie) { 793 int i; 794 char buf[1024]; 795 struct inverted_pam_userdata *ud = cookie->userdata; 796 char state; 797 798 /* Send over our responses */ 799 for (i = 0; i < cookie->num_msg; i++) { 800 if (cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_ON && 801 cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_OFF) 802 continue; 803 if (write(ud->responsefd[1], buf, 804 sprintf(buf, "%d\n", strlen(cookie->resp[i]->resp))) == -1) { 805 ipam_free_cookie(cookie); 806 return 0; 807 } 808 if (write(ud->responsefd[1], cookie->resp[i]->resp, 809 strlen(cookie->resp[i]->resp)) == -1) { 810 ipam_free_cookie(cookie); 811 return 0; 812 } 813 } 814 /* Find out what state we are changing to */ 815 if (read(ud->statefd[0], &state, 1) != 1) { 816 ipam_free_cookie(cookie); 817 return 0; 818 } 819 820 return state == STATE_AUTH_OK ? 1 : 0; 821} 822 | |
823#endif /* USE_PAM */ | 434#endif /* USE_PAM */ |