diff options
author | Colin Watson <cjwatson@debian.org> | 2016-08-06 10:49:58 +0100 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2016-08-06 10:49:58 +0100 |
commit | a8ed8d256b2e2c05b0c15565a7938028c5192277 (patch) | |
tree | 87abbdc914a38b43e4e5bb9581ad1f46eabbf88e /auth-pam.c | |
parent | f0329aac23c61e1a5197d6d57349a63f459bccb0 (diff) | |
parent | 99522ba7ec6963a05c04a156bf20e3ba3605987c (diff) |
Import openssh_7.3p1.orig.tar.gz
Diffstat (limited to 'auth-pam.c')
-rw-r--r-- | auth-pam.c | 107 |
1 files changed, 77 insertions, 30 deletions
diff --git a/auth-pam.c b/auth-pam.c index 8425af1ea..348fe370a 100644 --- a/auth-pam.c +++ b/auth-pam.c | |||
@@ -68,9 +68,9 @@ | |||
68 | 68 | ||
69 | /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */ | 69 | /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */ |
70 | #ifdef PAM_SUN_CODEBASE | 70 | #ifdef PAM_SUN_CODEBASE |
71 | # define sshpam_const /* Solaris, HP-UX, AIX */ | 71 | # define sshpam_const /* Solaris, HP-UX, SunOS */ |
72 | #else | 72 | #else |
73 | # define sshpam_const const /* LinuxPAM, OpenPAM */ | 73 | # define sshpam_const const /* LinuxPAM, OpenPAM, AIX */ |
74 | #endif | 74 | #endif |
75 | 75 | ||
76 | /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */ | 76 | /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */ |
@@ -154,9 +154,12 @@ sshpam_sigchld_handler(int sig) | |||
154 | <= 0) { | 154 | <= 0) { |
155 | /* PAM thread has not exitted, privsep slave must have */ | 155 | /* PAM thread has not exitted, privsep slave must have */ |
156 | kill(cleanup_ctxt->pam_thread, SIGTERM); | 156 | kill(cleanup_ctxt->pam_thread, SIGTERM); |
157 | if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0) | 157 | while (waitpid(cleanup_ctxt->pam_thread, |
158 | <= 0) | 158 | &sshpam_thread_status, 0) == -1) { |
159 | return; /* could not wait */ | 159 | if (errno == EINTR) |
160 | continue; | ||
161 | return; | ||
162 | } | ||
160 | } | 163 | } |
161 | if (WIFSIGNALED(sshpam_thread_status) && | 164 | if (WIFSIGNALED(sshpam_thread_status) && |
162 | WTERMSIG(sshpam_thread_status) == SIGTERM) | 165 | WTERMSIG(sshpam_thread_status) == SIGTERM) |
@@ -217,7 +220,11 @@ pthread_join(sp_pthread_t thread, void **value) | |||
217 | if (sshpam_thread_status != -1) | 220 | if (sshpam_thread_status != -1) |
218 | return (sshpam_thread_status); | 221 | return (sshpam_thread_status); |
219 | signal(SIGCHLD, sshpam_oldsig); | 222 | signal(SIGCHLD, sshpam_oldsig); |
220 | waitpid(thread, &status, 0); | 223 | while (waitpid(thread, &status, 0) == -1) { |
224 | if (errno == EINTR) | ||
225 | continue; | ||
226 | fatal("%s: waitpid: %s", __func__, strerror(errno)); | ||
227 | } | ||
221 | return (status); | 228 | return (status); |
222 | } | 229 | } |
223 | #endif | 230 | #endif |
@@ -229,10 +236,10 @@ static int sshpam_authenticated = 0; | |||
229 | static int sshpam_session_open = 0; | 236 | static int sshpam_session_open = 0; |
230 | static int sshpam_cred_established = 0; | 237 | static int sshpam_cred_established = 0; |
231 | static int sshpam_account_status = -1; | 238 | static int sshpam_account_status = -1; |
239 | static int sshpam_maxtries_reached = 0; | ||
232 | static char **sshpam_env = NULL; | 240 | static char **sshpam_env = NULL; |
233 | static Authctxt *sshpam_authctxt = NULL; | 241 | static Authctxt *sshpam_authctxt = NULL; |
234 | static const char *sshpam_password = NULL; | 242 | static const char *sshpam_password = NULL; |
235 | static char badpw[] = "\b\n\r\177INCORRECT"; | ||
236 | 243 | ||
237 | /* Some PAM implementations don't implement this */ | 244 | /* Some PAM implementations don't implement this */ |
238 | #ifndef HAVE_PAM_GETENVLIST | 245 | #ifndef HAVE_PAM_GETENVLIST |
@@ -365,17 +372,6 @@ sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, | |||
365 | for (i = 0; i < n; ++i) { | 372 | for (i = 0; i < n; ++i) { |
366 | switch (PAM_MSG_MEMBER(msg, i, msg_style)) { | 373 | switch (PAM_MSG_MEMBER(msg, i, msg_style)) { |
367 | case PAM_PROMPT_ECHO_OFF: | 374 | case PAM_PROMPT_ECHO_OFF: |
368 | buffer_put_cstring(&buffer, | ||
369 | PAM_MSG_MEMBER(msg, i, msg)); | ||
370 | if (ssh_msg_send(ctxt->pam_csock, | ||
371 | PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) | ||
372 | goto fail; | ||
373 | if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) | ||
374 | goto fail; | ||
375 | if (buffer_get_char(&buffer) != PAM_AUTHTOK) | ||
376 | goto fail; | ||
377 | reply[i].resp = buffer_get_string(&buffer, NULL); | ||
378 | break; | ||
379 | case PAM_PROMPT_ECHO_ON: | 375 | case PAM_PROMPT_ECHO_ON: |
380 | buffer_put_cstring(&buffer, | 376 | buffer_put_cstring(&buffer, |
381 | PAM_MSG_MEMBER(msg, i, msg)); | 377 | PAM_MSG_MEMBER(msg, i, msg)); |
@@ -389,12 +385,6 @@ sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, | |||
389 | reply[i].resp = buffer_get_string(&buffer, NULL); | 385 | reply[i].resp = buffer_get_string(&buffer, NULL); |
390 | break; | 386 | break; |
391 | case PAM_ERROR_MSG: | 387 | case PAM_ERROR_MSG: |
392 | buffer_put_cstring(&buffer, | ||
393 | PAM_MSG_MEMBER(msg, i, msg)); | ||
394 | if (ssh_msg_send(ctxt->pam_csock, | ||
395 | PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) | ||
396 | goto fail; | ||
397 | break; | ||
398 | case PAM_TEXT_INFO: | 388 | case PAM_TEXT_INFO: |
399 | buffer_put_cstring(&buffer, | 389 | buffer_put_cstring(&buffer, |
400 | PAM_MSG_MEMBER(msg, i, msg)); | 390 | PAM_MSG_MEMBER(msg, i, msg)); |
@@ -468,6 +458,8 @@ sshpam_thread(void *ctxtp) | |||
468 | if (sshpam_err != PAM_SUCCESS) | 458 | if (sshpam_err != PAM_SUCCESS) |
469 | goto auth_fail; | 459 | goto auth_fail; |
470 | sshpam_err = pam_authenticate(sshpam_handle, flags); | 460 | sshpam_err = pam_authenticate(sshpam_handle, flags); |
461 | if (sshpam_err == PAM_MAXTRIES) | ||
462 | sshpam_set_maxtries_reached(1); | ||
471 | if (sshpam_err != PAM_SUCCESS) | 463 | if (sshpam_err != PAM_SUCCESS) |
472 | goto auth_fail; | 464 | goto auth_fail; |
473 | 465 | ||
@@ -519,6 +511,8 @@ sshpam_thread(void *ctxtp) | |||
519 | /* XXX - can't do much about an error here */ | 511 | /* XXX - can't do much about an error here */ |
520 | if (sshpam_err == PAM_ACCT_EXPIRED) | 512 | if (sshpam_err == PAM_ACCT_EXPIRED) |
521 | ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer); | 513 | ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer); |
514 | else if (sshpam_maxtries_reached) | ||
515 | ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, &buffer); | ||
522 | else | 516 | else |
523 | ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer); | 517 | ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer); |
524 | buffer_free(&buffer); | 518 | buffer_free(&buffer); |
@@ -624,6 +618,7 @@ sshpam_init(Authctxt *authctxt) | |||
624 | extern char *__progname; | 618 | extern char *__progname; |
625 | const char *pam_rhost, *pam_user, *user = authctxt->user; | 619 | const char *pam_rhost, *pam_user, *user = authctxt->user; |
626 | const char **ptr_pam_user = &pam_user; | 620 | const char **ptr_pam_user = &pam_user; |
621 | struct ssh *ssh = active_state; /* XXX */ | ||
627 | 622 | ||
628 | if (sshpam_handle != NULL) { | 623 | if (sshpam_handle != NULL) { |
629 | /* We already have a PAM context; check if the user matches */ | 624 | /* We already have a PAM context; check if the user matches */ |
@@ -644,7 +639,7 @@ sshpam_init(Authctxt *authctxt) | |||
644 | sshpam_handle = NULL; | 639 | sshpam_handle = NULL; |
645 | return (-1); | 640 | return (-1); |
646 | } | 641 | } |
647 | pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns); | 642 | pam_rhost = auth_get_canonical_hostname(ssh, options.use_dns); |
648 | debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost); | 643 | debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost); |
649 | sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost); | 644 | sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost); |
650 | if (sshpam_err != PAM_SUCCESS) { | 645 | if (sshpam_err != PAM_SUCCESS) { |
@@ -715,6 +710,7 @@ static int | |||
715 | sshpam_query(void *ctx, char **name, char **info, | 710 | sshpam_query(void *ctx, char **name, char **info, |
716 | u_int *num, char ***prompts, u_int **echo_on) | 711 | u_int *num, char ***prompts, u_int **echo_on) |
717 | { | 712 | { |
713 | struct ssh *ssh = active_state; /* XXX */ | ||
718 | Buffer buffer; | 714 | Buffer buffer; |
719 | struct pam_ctxt *ctxt = ctx; | 715 | struct pam_ctxt *ctxt = ctx; |
720 | size_t plen; | 716 | size_t plen; |
@@ -757,7 +753,11 @@ sshpam_query(void *ctx, char **name, char **info, | |||
757 | free(msg); | 753 | free(msg); |
758 | break; | 754 | break; |
759 | case PAM_ACCT_EXPIRED: | 755 | case PAM_ACCT_EXPIRED: |
760 | sshpam_account_status = 0; | 756 | case PAM_MAXTRIES: |
757 | if (type == PAM_ACCT_EXPIRED) | ||
758 | sshpam_account_status = 0; | ||
759 | if (type == PAM_MAXTRIES) | ||
760 | sshpam_set_maxtries_reached(1); | ||
761 | /* FALLTHROUGH */ | 761 | /* FALLTHROUGH */ |
762 | case PAM_AUTH_ERR: | 762 | case PAM_AUTH_ERR: |
763 | debug3("PAM: %s", pam_strerror(sshpam_handle, type)); | 763 | debug3("PAM: %s", pam_strerror(sshpam_handle, type)); |
@@ -797,7 +797,7 @@ sshpam_query(void *ctx, char **name, char **info, | |||
797 | error("PAM: %s for %s%.100s from %.100s", msg, | 797 | error("PAM: %s for %s%.100s from %.100s", msg, |
798 | sshpam_authctxt->valid ? "" : "illegal user ", | 798 | sshpam_authctxt->valid ? "" : "illegal user ", |
799 | sshpam_authctxt->user, | 799 | sshpam_authctxt->user, |
800 | get_remote_name_or_ip(utmp_len, options.use_dns)); | 800 | auth_get_canonical_hostname(ssh, options.use_dns)); |
801 | /* FALLTHROUGH */ | 801 | /* FALLTHROUGH */ |
802 | default: | 802 | default: |
803 | *num = 0; | 803 | *num = 0; |
@@ -810,12 +810,35 @@ sshpam_query(void *ctx, char **name, char **info, | |||
810 | return (-1); | 810 | return (-1); |
811 | } | 811 | } |
812 | 812 | ||
813 | /* | ||
814 | * Returns a junk password of identical length to that the user supplied. | ||
815 | * Used to mitigate timing attacks against crypt(3)/PAM stacks that | ||
816 | * vary processing time in proportion to password length. | ||
817 | */ | ||
818 | static char * | ||
819 | fake_password(const char *wire_password) | ||
820 | { | ||
821 | const char junk[] = "\b\n\r\177INCORRECT"; | ||
822 | char *ret = NULL; | ||
823 | size_t i, l = wire_password != NULL ? strlen(wire_password) : 0; | ||
824 | |||
825 | if (l >= INT_MAX) | ||
826 | fatal("%s: password length too long: %zu", __func__, l); | ||
827 | |||
828 | ret = malloc(l + 1); | ||
829 | for (i = 0; i < l; i++) | ||
830 | ret[i] = junk[i % (sizeof(junk) - 1)]; | ||
831 | ret[i] = '\0'; | ||
832 | return ret; | ||
833 | } | ||
834 | |||
813 | /* XXX - see also comment in auth-chall.c:verify_response */ | 835 | /* XXX - see also comment in auth-chall.c:verify_response */ |
814 | static int | 836 | static int |
815 | sshpam_respond(void *ctx, u_int num, char **resp) | 837 | sshpam_respond(void *ctx, u_int num, char **resp) |
816 | { | 838 | { |
817 | Buffer buffer; | 839 | Buffer buffer; |
818 | struct pam_ctxt *ctxt = ctx; | 840 | struct pam_ctxt *ctxt = ctx; |
841 | char *fake; | ||
819 | 842 | ||
820 | debug2("PAM: %s entering, %u responses", __func__, num); | 843 | debug2("PAM: %s entering, %u responses", __func__, num); |
821 | switch (ctxt->pam_done) { | 844 | switch (ctxt->pam_done) { |
@@ -836,8 +859,11 @@ sshpam_respond(void *ctx, u_int num, char **resp) | |||
836 | (sshpam_authctxt->pw->pw_uid != 0 || | 859 | (sshpam_authctxt->pw->pw_uid != 0 || |
837 | options.permit_root_login == PERMIT_YES)) | 860 | options.permit_root_login == PERMIT_YES)) |
838 | buffer_put_cstring(&buffer, *resp); | 861 | buffer_put_cstring(&buffer, *resp); |
839 | else | 862 | else { |
840 | buffer_put_cstring(&buffer, badpw); | 863 | fake = fake_password(*resp); |
864 | buffer_put_cstring(&buffer, fake); | ||
865 | free(fake); | ||
866 | } | ||
841 | if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { | 867 | if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { |
842 | buffer_free(&buffer); | 868 | buffer_free(&buffer); |
843 | return (-1); | 869 | return (-1); |
@@ -1181,6 +1207,7 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) | |||
1181 | { | 1207 | { |
1182 | int flags = (options.permit_empty_passwd == 0 ? | 1208 | int flags = (options.permit_empty_passwd == 0 ? |
1183 | PAM_DISALLOW_NULL_AUTHTOK : 0); | 1209 | PAM_DISALLOW_NULL_AUTHTOK : 0); |
1210 | char *fake = NULL; | ||
1184 | 1211 | ||
1185 | if (!options.use_pam || sshpam_handle == NULL) | 1212 | if (!options.use_pam || sshpam_handle == NULL) |
1186 | fatal("PAM: %s called when PAM disabled or failed to " | 1213 | fatal("PAM: %s called when PAM disabled or failed to " |
@@ -1196,7 +1223,7 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) | |||
1196 | */ | 1223 | */ |
1197 | if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && | 1224 | if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && |
1198 | options.permit_root_login != PERMIT_YES)) | 1225 | options.permit_root_login != PERMIT_YES)) |
1199 | sshpam_password = badpw; | 1226 | sshpam_password = fake = fake_password(password); |
1200 | 1227 | ||
1201 | sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, | 1228 | sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, |
1202 | (const void *)&passwd_conv); | 1229 | (const void *)&passwd_conv); |
@@ -1206,6 +1233,9 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) | |||
1206 | 1233 | ||
1207 | sshpam_err = pam_authenticate(sshpam_handle, flags); | 1234 | sshpam_err = pam_authenticate(sshpam_handle, flags); |
1208 | sshpam_password = NULL; | 1235 | sshpam_password = NULL; |
1236 | free(fake); | ||
1237 | if (sshpam_err == PAM_MAXTRIES) | ||
1238 | sshpam_set_maxtries_reached(1); | ||
1209 | if (sshpam_err == PAM_SUCCESS && authctxt->valid) { | 1239 | if (sshpam_err == PAM_SUCCESS && authctxt->valid) { |
1210 | debug("PAM: password authentication accepted for %.100s", | 1240 | debug("PAM: password authentication accepted for %.100s", |
1211 | authctxt->user); | 1241 | authctxt->user); |
@@ -1217,4 +1247,21 @@ sshpam_auth_passwd(Authctxt *authctxt, const char *password) | |||
1217 | return 0; | 1247 | return 0; |
1218 | } | 1248 | } |
1219 | } | 1249 | } |
1250 | |||
1251 | int | ||
1252 | sshpam_get_maxtries_reached(void) | ||
1253 | { | ||
1254 | return sshpam_maxtries_reached; | ||
1255 | } | ||
1256 | |||
1257 | void | ||
1258 | sshpam_set_maxtries_reached(int reached) | ||
1259 | { | ||
1260 | if (reached == 0 || sshpam_maxtries_reached) | ||
1261 | return; | ||
1262 | sshpam_maxtries_reached = 1; | ||
1263 | options.password_authentication = 0; | ||
1264 | options.kbd_interactive_authentication = 0; | ||
1265 | options.challenge_response_authentication = 0; | ||
1266 | } | ||
1220 | #endif /* USE_PAM */ | 1267 | #endif /* USE_PAM */ |