summaryrefslogtreecommitdiff
path: root/auth-pam.c
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2016-08-06 10:49:58 +0100
committerColin Watson <cjwatson@debian.org>2016-08-06 10:49:58 +0100
commita8ed8d256b2e2c05b0c15565a7938028c5192277 (patch)
tree87abbdc914a38b43e4e5bb9581ad1f46eabbf88e /auth-pam.c
parentf0329aac23c61e1a5197d6d57349a63f459bccb0 (diff)
parent99522ba7ec6963a05c04a156bf20e3ba3605987c (diff)
Import openssh_7.3p1.orig.tar.gz
Diffstat (limited to 'auth-pam.c')
-rw-r--r--auth-pam.c107
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;
229static int sshpam_session_open = 0; 236static int sshpam_session_open = 0;
230static int sshpam_cred_established = 0; 237static int sshpam_cred_established = 0;
231static int sshpam_account_status = -1; 238static int sshpam_account_status = -1;
239static int sshpam_maxtries_reached = 0;
232static char **sshpam_env = NULL; 240static char **sshpam_env = NULL;
233static Authctxt *sshpam_authctxt = NULL; 241static Authctxt *sshpam_authctxt = NULL;
234static const char *sshpam_password = NULL; 242static const char *sshpam_password = NULL;
235static 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
715sshpam_query(void *ctx, char **name, char **info, 710sshpam_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 */
818static char *
819fake_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 */
814static int 836static int
815sshpam_respond(void *ctx, u_int num, char **resp) 837sshpam_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
1251int
1252sshpam_get_maxtries_reached(void)
1253{
1254 return sshpam_maxtries_reached;
1255}
1256
1257void
1258sshpam_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 */