diff options
Diffstat (limited to 'auth-pam.c')
-rw-r--r-- | auth-pam.c | 194 |
1 files changed, 177 insertions, 17 deletions
diff --git a/auth-pam.c b/auth-pam.c index 9be57dacd..147f4f8bb 100644 --- a/auth-pam.c +++ b/auth-pam.c | |||
@@ -28,10 +28,26 @@ | |||
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. | 29 | * SUCH DAMAGE. |
30 | */ | 30 | */ |
31 | /* | ||
32 | * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org> | ||
33 | * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au> | ||
34 | * | ||
35 | * Permission to use, copy, modify, and distribute this software for any | ||
36 | * purpose with or without fee is hereby granted, provided that the above | ||
37 | * copyright notice and this permission notice appear in all copies. | ||
38 | * | ||
39 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
40 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
41 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
42 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
43 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
44 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
45 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
46 | */ | ||
31 | 47 | ||
32 | /* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */ | 48 | /* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */ |
33 | #include "includes.h" | 49 | #include "includes.h" |
34 | RCSID("$Id: auth-pam.c,v 1.100 2004/04/18 01:00:26 dtucker Exp $"); | 50 | RCSID("$Id: auth-pam.c,v 1.114 2004/08/16 13:12:06 dtucker Exp $"); |
35 | 51 | ||
36 | #ifdef USE_PAM | 52 | #ifdef USE_PAM |
37 | #if defined(HAVE_SECURITY_PAM_APPL_H) | 53 | #if defined(HAVE_SECURITY_PAM_APPL_H) |
@@ -49,7 +65,7 @@ RCSID("$Id: auth-pam.c,v 1.100 2004/04/18 01:00:26 dtucker Exp $"); | |||
49 | #include "monitor_wrap.h" | 65 | #include "monitor_wrap.h" |
50 | #include "msg.h" | 66 | #include "msg.h" |
51 | #include "packet.h" | 67 | #include "packet.h" |
52 | #include "readpass.h" | 68 | #include "misc.h" |
53 | #include "servconf.h" | 69 | #include "servconf.h" |
54 | #include "ssh2.h" | 70 | #include "ssh2.h" |
55 | #include "xmalloc.h" | 71 | #include "xmalloc.h" |
@@ -170,6 +186,7 @@ static int sshpam_account_status = -1; | |||
170 | static char **sshpam_env = NULL; | 186 | static char **sshpam_env = NULL; |
171 | static Authctxt *sshpam_authctxt = NULL; | 187 | static Authctxt *sshpam_authctxt = NULL; |
172 | static char badpw[] = "\b\n\r\177INCORRECT"; | 188 | static char badpw[] = "\b\n\r\177INCORRECT"; |
189 | static const char *sshpam_password = NULL; | ||
173 | 190 | ||
174 | /* Some PAM implementations don't implement this */ | 191 | /* Some PAM implementations don't implement this */ |
175 | #ifndef HAVE_PAM_GETENVLIST | 192 | #ifndef HAVE_PAM_GETENVLIST |
@@ -185,8 +202,33 @@ pam_getenvlist(pam_handle_t *pamh) | |||
185 | } | 202 | } |
186 | #endif | 203 | #endif |
187 | 204 | ||
205 | /* | ||
206 | * Some platforms, notably Solaris, do not enforce password complexity | ||
207 | * rules during pam_chauthtok() if the real uid of the calling process | ||
208 | * is 0, on the assumption that it's being called by "passwd" run by root. | ||
209 | * This wraps pam_chauthtok and sets/restore the real uid so PAM will do | ||
210 | * the right thing. | ||
211 | */ | ||
212 | #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID | ||
213 | static int | ||
214 | sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags) | ||
215 | { | ||
216 | int result; | ||
217 | |||
218 | if (sshpam_authctxt == NULL) | ||
219 | fatal("PAM: sshpam_authctxt not initialized"); | ||
220 | if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1) | ||
221 | fatal("%s: setreuid failed: %s", __func__, strerror(errno)); | ||
222 | result = pam_chauthtok(pamh, flags); | ||
223 | if (setreuid(0, -1) == -1) | ||
224 | fatal("%s: setreuid failed: %s", __func__, strerror(errno)); | ||
225 | return result; | ||
226 | } | ||
227 | # define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b))) | ||
228 | #endif | ||
229 | |||
188 | void | 230 | void |
189 | pam_password_change_required(int reqd) | 231 | sshpam_password_change_required(int reqd) |
190 | { | 232 | { |
191 | debug3("%s %d", __func__, reqd); | 233 | debug3("%s %d", __func__, reqd); |
192 | if (sshpam_authctxt == NULL) | 234 | if (sshpam_authctxt == NULL) |
@@ -216,7 +258,7 @@ import_environments(Buffer *b) | |||
216 | #ifndef USE_POSIX_THREADS | 258 | #ifndef USE_POSIX_THREADS |
217 | /* Import variables set by do_pam_account */ | 259 | /* Import variables set by do_pam_account */ |
218 | sshpam_account_status = buffer_get_int(b); | 260 | sshpam_account_status = buffer_get_int(b); |
219 | pam_password_change_required(buffer_get_int(b)); | 261 | sshpam_password_change_required(buffer_get_int(b)); |
220 | 262 | ||
221 | /* Import environment from subprocess */ | 263 | /* Import environment from subprocess */ |
222 | num_env = buffer_get_int(b); | 264 | num_env = buffer_get_int(b); |
@@ -248,7 +290,7 @@ import_environments(Buffer *b) | |||
248 | * Conversation function for authentication thread. | 290 | * Conversation function for authentication thread. |
249 | */ | 291 | */ |
250 | static int | 292 | static int |
251 | sshpam_thread_conv(int n, const struct pam_message **msg, | 293 | sshpam_thread_conv(int n, struct pam_message **msg, |
252 | struct pam_response **resp, void *data) | 294 | struct pam_response **resp, void *data) |
253 | { | 295 | { |
254 | Buffer buffer; | 296 | Buffer buffer; |
@@ -259,6 +301,10 @@ sshpam_thread_conv(int n, const struct pam_message **msg, | |||
259 | debug3("PAM: %s entering, %d messages", __func__, n); | 301 | debug3("PAM: %s entering, %d messages", __func__, n); |
260 | *resp = NULL; | 302 | *resp = NULL; |
261 | 303 | ||
304 | if (data == NULL) { | ||
305 | error("PAM: conversation function passed a null context"); | ||
306 | return (PAM_CONV_ERR); | ||
307 | } | ||
262 | ctxt = data; | 308 | ctxt = data; |
263 | if (n <= 0 || n > PAM_MAX_NUM_MSG) | 309 | if (n <= 0 || n > PAM_MAX_NUM_MSG) |
264 | return (PAM_CONV_ERR); | 310 | return (PAM_CONV_ERR); |
@@ -336,15 +382,21 @@ sshpam_thread(void *ctxtp) | |||
336 | struct pam_ctxt *ctxt = ctxtp; | 382 | struct pam_ctxt *ctxt = ctxtp; |
337 | Buffer buffer; | 383 | Buffer buffer; |
338 | struct pam_conv sshpam_conv; | 384 | struct pam_conv sshpam_conv; |
385 | int flags = (options.permit_empty_passwd == 0 ? | ||
386 | PAM_DISALLOW_NULL_AUTHTOK : 0); | ||
339 | #ifndef USE_POSIX_THREADS | 387 | #ifndef USE_POSIX_THREADS |
340 | extern char **environ; | 388 | extern char **environ; |
341 | char **env_from_pam; | 389 | char **env_from_pam; |
342 | u_int i; | 390 | u_int i; |
343 | const char *pam_user; | 391 | const char *pam_user; |
344 | 392 | ||
345 | pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user); | 393 | pam_get_item(sshpam_handle, PAM_USER, (void **)&pam_user); |
346 | setproctitle("%s [pam]", pam_user); | ||
347 | environ[0] = NULL; | 394 | environ[0] = NULL; |
395 | |||
396 | if (sshpam_authctxt != NULL) { | ||
397 | setproctitle("%s [pam]", | ||
398 | sshpam_authctxt->valid ? pam_user : "unknown"); | ||
399 | } | ||
348 | #endif | 400 | #endif |
349 | 401 | ||
350 | sshpam_conv.conv = sshpam_thread_conv; | 402 | sshpam_conv.conv = sshpam_thread_conv; |
@@ -358,7 +410,7 @@ sshpam_thread(void *ctxtp) | |||
358 | (const void *)&sshpam_conv); | 410 | (const void *)&sshpam_conv); |
359 | if (sshpam_err != PAM_SUCCESS) | 411 | if (sshpam_err != PAM_SUCCESS) |
360 | goto auth_fail; | 412 | goto auth_fail; |
361 | sshpam_err = pam_authenticate(sshpam_handle, 0); | 413 | sshpam_err = pam_authenticate(sshpam_handle, flags); |
362 | if (sshpam_err != PAM_SUCCESS) | 414 | if (sshpam_err != PAM_SUCCESS) |
363 | goto auth_fail; | 415 | goto auth_fail; |
364 | 416 | ||
@@ -370,7 +422,7 @@ sshpam_thread(void *ctxtp) | |||
370 | PAM_CHANGE_EXPIRED_AUTHTOK); | 422 | PAM_CHANGE_EXPIRED_AUTHTOK); |
371 | if (sshpam_err != PAM_SUCCESS) | 423 | if (sshpam_err != PAM_SUCCESS) |
372 | goto auth_fail; | 424 | goto auth_fail; |
373 | pam_password_change_required(0); | 425 | sshpam_password_change_required(0); |
374 | } | 426 | } |
375 | } | 427 | } |
376 | 428 | ||
@@ -430,7 +482,7 @@ sshpam_thread_cleanup(void) | |||
430 | } | 482 | } |
431 | 483 | ||
432 | static int | 484 | static int |
433 | sshpam_null_conv(int n, const struct pam_message **msg, | 485 | sshpam_null_conv(int n, struct pam_message **msg, |
434 | struct pam_response **resp, void *data) | 486 | struct pam_response **resp, void *data) |
435 | { | 487 | { |
436 | debug3("PAM: %s entering, %d messages", __func__, n); | 488 | debug3("PAM: %s entering, %d messages", __func__, n); |
@@ -468,7 +520,7 @@ sshpam_init(Authctxt *authctxt) | |||
468 | if (sshpam_handle != NULL) { | 520 | if (sshpam_handle != NULL) { |
469 | /* We already have a PAM context; check if the user matches */ | 521 | /* We already have a PAM context; check if the user matches */ |
470 | sshpam_err = pam_get_item(sshpam_handle, | 522 | sshpam_err = pam_get_item(sshpam_handle, |
471 | PAM_USER, (const void **)&pam_user); | 523 | PAM_USER, (void **)&pam_user); |
472 | if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) | 524 | if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) |
473 | return (0); | 525 | return (0); |
474 | pam_end(sshpam_handle, sshpam_err); | 526 | pam_end(sshpam_handle, sshpam_err); |
@@ -725,7 +777,7 @@ do_pam_account(void) | |||
725 | } | 777 | } |
726 | 778 | ||
727 | if (sshpam_err == PAM_NEW_AUTHTOK_REQD) | 779 | if (sshpam_err == PAM_NEW_AUTHTOK_REQD) |
728 | pam_password_change_required(1); | 780 | sshpam_password_change_required(1); |
729 | 781 | ||
730 | sshpam_account_status = 1; | 782 | sshpam_account_status = 1; |
731 | return (sshpam_account_status); | 783 | return (sshpam_account_status); |
@@ -771,7 +823,7 @@ do_pam_setcred(int init) | |||
771 | } | 823 | } |
772 | 824 | ||
773 | static int | 825 | static int |
774 | pam_tty_conv(int n, const struct pam_message **msg, | 826 | sshpam_tty_conv(int n, struct pam_message **msg, |
775 | struct pam_response **resp, void *data) | 827 | struct pam_response **resp, void *data) |
776 | { | 828 | { |
777 | char input[PAM_MAX_MSG_SIZE]; | 829 | char input[PAM_MAX_MSG_SIZE]; |
@@ -800,7 +852,8 @@ pam_tty_conv(int n, const struct pam_message **msg, | |||
800 | case PAM_PROMPT_ECHO_ON: | 852 | case PAM_PROMPT_ECHO_ON: |
801 | fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); | 853 | fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); |
802 | fgets(input, sizeof input, stdin); | 854 | fgets(input, sizeof input, stdin); |
803 | reply[i].resp = xstrdup(input); | 855 | if ((reply[i].resp = strdup(input)) == NULL) |
856 | goto fail; | ||
804 | reply[i].resp_retcode = PAM_SUCCESS; | 857 | reply[i].resp_retcode = PAM_SUCCESS; |
805 | break; | 858 | break; |
806 | case PAM_ERROR_MSG: | 859 | case PAM_ERROR_MSG: |
@@ -824,7 +877,7 @@ pam_tty_conv(int n, const struct pam_message **msg, | |||
824 | return (PAM_CONV_ERR); | 877 | return (PAM_CONV_ERR); |
825 | } | 878 | } |
826 | 879 | ||
827 | static struct pam_conv tty_conv = { pam_tty_conv, NULL }; | 880 | static struct pam_conv tty_conv = { sshpam_tty_conv, NULL }; |
828 | 881 | ||
829 | /* | 882 | /* |
830 | * XXX this should be done in the authentication phase, but ssh1 doesn't | 883 | * XXX this should be done in the authentication phase, but ssh1 doesn't |
@@ -848,7 +901,7 @@ do_pam_chauthtok(void) | |||
848 | } | 901 | } |
849 | 902 | ||
850 | static int | 903 | static int |
851 | pam_store_conv(int n, const struct pam_message **msg, | 904 | sshpam_store_conv(int n, struct pam_message **msg, |
852 | struct pam_response **resp, void *data) | 905 | struct pam_response **resp, void *data) |
853 | { | 906 | { |
854 | struct pam_response *reply; | 907 | struct pam_response *reply; |
@@ -890,7 +943,7 @@ pam_store_conv(int n, const struct pam_message **msg, | |||
890 | return (PAM_CONV_ERR); | 943 | return (PAM_CONV_ERR); |
891 | } | 944 | } |
892 | 945 | ||
893 | static struct pam_conv store_conv = { pam_store_conv, NULL }; | 946 | static struct pam_conv store_conv = { sshpam_store_conv, NULL }; |
894 | 947 | ||
895 | void | 948 | void |
896 | do_pam_session(void) | 949 | do_pam_session(void) |
@@ -957,4 +1010,111 @@ free_pam_environment(char **env) | |||
957 | xfree(env); | 1010 | xfree(env); |
958 | } | 1011 | } |
959 | 1012 | ||
1013 | /* | ||
1014 | * "Blind" conversation function for password authentication. Assumes that | ||
1015 | * echo-off prompts are for the password and stores messages for later | ||
1016 | * display. | ||
1017 | */ | ||
1018 | static int | ||
1019 | sshpam_passwd_conv(int n, struct pam_message **msg, | ||
1020 | struct pam_response **resp, void *data) | ||
1021 | { | ||
1022 | struct pam_response *reply; | ||
1023 | int i; | ||
1024 | size_t len; | ||
1025 | |||
1026 | debug3("PAM: %s called with %d messages", __func__, n); | ||
1027 | |||
1028 | *resp = NULL; | ||
1029 | |||
1030 | if (n <= 0 || n > PAM_MAX_NUM_MSG) | ||
1031 | return (PAM_CONV_ERR); | ||
1032 | |||
1033 | if ((reply = malloc(n * sizeof(*reply))) == NULL) | ||
1034 | return (PAM_CONV_ERR); | ||
1035 | memset(reply, 0, n * sizeof(*reply)); | ||
1036 | |||
1037 | for (i = 0; i < n; ++i) { | ||
1038 | switch (PAM_MSG_MEMBER(msg, i, msg_style)) { | ||
1039 | case PAM_PROMPT_ECHO_OFF: | ||
1040 | if (sshpam_password == NULL) | ||
1041 | goto fail; | ||
1042 | if ((reply[i].resp = strdup(sshpam_password)) == NULL) | ||
1043 | goto fail; | ||
1044 | reply[i].resp_retcode = PAM_SUCCESS; | ||
1045 | break; | ||
1046 | case PAM_ERROR_MSG: | ||
1047 | case PAM_TEXT_INFO: | ||
1048 | len = strlen(PAM_MSG_MEMBER(msg, i, msg)); | ||
1049 | if (len > 0) { | ||
1050 | buffer_append(&loginmsg, | ||
1051 | PAM_MSG_MEMBER(msg, i, msg), len); | ||
1052 | buffer_append(&loginmsg, "\n", 1); | ||
1053 | } | ||
1054 | if ((reply[i].resp = strdup("")) == NULL) | ||
1055 | goto fail; | ||
1056 | reply[i].resp_retcode = PAM_SUCCESS; | ||
1057 | break; | ||
1058 | default: | ||
1059 | goto fail; | ||
1060 | } | ||
1061 | } | ||
1062 | *resp = reply; | ||
1063 | return (PAM_SUCCESS); | ||
1064 | |||
1065 | fail: | ||
1066 | for(i = 0; i < n; i++) { | ||
1067 | if (reply[i].resp != NULL) | ||
1068 | xfree(reply[i].resp); | ||
1069 | } | ||
1070 | xfree(reply); | ||
1071 | return (PAM_CONV_ERR); | ||
1072 | } | ||
1073 | |||
1074 | static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL }; | ||
1075 | |||
1076 | /* | ||
1077 | * Attempt password authentication via PAM | ||
1078 | */ | ||
1079 | int | ||
1080 | sshpam_auth_passwd(Authctxt *authctxt, const char *password) | ||
1081 | { | ||
1082 | int flags = (options.permit_empty_passwd == 0 ? | ||
1083 | PAM_DISALLOW_NULL_AUTHTOK : 0); | ||
1084 | |||
1085 | if (!options.use_pam || sshpam_handle == NULL) | ||
1086 | fatal("PAM: %s called when PAM disabled or failed to " | ||
1087 | "initialise.", __func__); | ||
1088 | |||
1089 | sshpam_password = password; | ||
1090 | sshpam_authctxt = authctxt; | ||
1091 | |||
1092 | /* | ||
1093 | * If the user logging in is invalid, or is root but is not permitted | ||
1094 | * by PermitRootLogin, use an invalid password to prevent leaking | ||
1095 | * information via timing (eg if the PAM config has a delay on fail). | ||
1096 | */ | ||
1097 | if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && | ||
1098 | options.permit_root_login != PERMIT_YES)) | ||
1099 | sshpam_password = badpw; | ||
1100 | |||
1101 | sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, | ||
1102 | (const void *)&passwd_conv); | ||
1103 | if (sshpam_err != PAM_SUCCESS) | ||
1104 | fatal("PAM: %s: failed to set PAM_CONV: %s", __func__, | ||
1105 | pam_strerror(sshpam_handle, sshpam_err)); | ||
1106 | |||
1107 | sshpam_err = pam_authenticate(sshpam_handle, flags); | ||
1108 | sshpam_password = NULL; | ||
1109 | if (sshpam_err == PAM_SUCCESS && authctxt->valid) { | ||
1110 | debug("PAM: password authentication accepted for %.100s", | ||
1111 | authctxt->user); | ||
1112 | return 1; | ||
1113 | } else { | ||
1114 | debug("PAM: password authentication failed for %.100s: %s", | ||
1115 | authctxt->valid ? authctxt->user : "an illegal user", | ||
1116 | pam_strerror(sshpam_handle, sshpam_err)); | ||
1117 | return 0; | ||
1118 | } | ||
1119 | } | ||
960 | #endif /* USE_PAM */ | 1120 | #endif /* USE_PAM */ |