020-Wait-to-fail-invalid-usernames.patch 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. From 52adbb34c32d3e2e1bcdb941e20a6f81138b8248 Mon Sep 17 00:00:00 2001
  2. From: Matt Johnston <matt@ucc.asn.au>
  3. Date: Thu, 23 Aug 2018 23:43:12 +0800
  4. Subject: [PATCH 2/2] Wait to fail invalid usernames
  5. ---
  6. auth.h | 6 +++---
  7. svr-auth.c | 19 +++++--------------
  8. svr-authpam.c | 26 ++++++++++++++++++++++----
  9. svr-authpasswd.c | 27 ++++++++++++++-------------
  10. svr-authpubkey.c | 11 ++++++++++-
  11. 5 files changed, 54 insertions(+), 35 deletions(-)
  12. --- a/auth.h
  13. +++ b/auth.h
  14. @@ -37,9 +37,9 @@ void recv_msg_userauth_request(void);
  15. void send_msg_userauth_failure(int partial, int incrfail);
  16. void send_msg_userauth_success(void);
  17. void send_msg_userauth_banner(buffer *msg);
  18. -void svr_auth_password(void);
  19. -void svr_auth_pubkey(void);
  20. -void svr_auth_pam(void);
  21. +void svr_auth_password(int valid_user);
  22. +void svr_auth_pubkey(int valid_user);
  23. +void svr_auth_pam(int valid_user);
  24. #ifdef ENABLE_SVR_PUBKEY_OPTIONS
  25. int svr_pubkey_allows_agentfwd(void);
  26. --- a/svr-auth.c
  27. +++ b/svr-auth.c
  28. @@ -176,10 +176,8 @@ void recv_msg_userauth_request() {
  29. if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
  30. strncmp(methodname, AUTH_METHOD_PASSWORD,
  31. AUTH_METHOD_PASSWORD_LEN) == 0) {
  32. - if (valid_user) {
  33. - svr_auth_password();
  34. - goto out;
  35. - }
  36. + svr_auth_password(valid_user);
  37. + goto out;
  38. }
  39. }
  40. #endif
  41. @@ -191,10 +189,8 @@ void recv_msg_userauth_request() {
  42. if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
  43. strncmp(methodname, AUTH_METHOD_PASSWORD,
  44. AUTH_METHOD_PASSWORD_LEN) == 0) {
  45. - if (valid_user) {
  46. - svr_auth_pam();
  47. - goto out;
  48. - }
  49. + svr_auth_pam(valid_user);
  50. + goto out;
  51. }
  52. }
  53. #endif
  54. @@ -204,12 +200,7 @@ void recv_msg_userauth_request() {
  55. if (methodlen == AUTH_METHOD_PUBKEY_LEN &&
  56. strncmp(methodname, AUTH_METHOD_PUBKEY,
  57. AUTH_METHOD_PUBKEY_LEN) == 0) {
  58. - if (valid_user) {
  59. - svr_auth_pubkey();
  60. - } else {
  61. - /* pubkey has no failure delay */
  62. - send_msg_userauth_failure(0, 0);
  63. - }
  64. + svr_auth_pubkey(valid_user);
  65. goto out;
  66. }
  67. #endif
  68. --- a/svr-authpam.c
  69. +++ b/svr-authpam.c
  70. @@ -178,13 +178,14 @@ pamConvFunc(int num_msg,
  71. * Keyboard interactive would be a lot nicer, but since PAM is synchronous, it
  72. * gets very messy trying to send the interactive challenges, and read the
  73. * interactive responses, over the network. */
  74. -void svr_auth_pam() {
  75. +void svr_auth_pam(int valid_user) {
  76. struct UserDataS userData = {NULL, NULL};
  77. struct pam_conv pamConv = {
  78. pamConvFunc,
  79. &userData /* submitted to pamvConvFunc as appdata_ptr */
  80. };
  81. + const char* printable_user = NULL;
  82. pam_handle_t* pamHandlep = NULL;
  83. @@ -204,12 +205,23 @@ void svr_auth_pam() {
  84. password = buf_getstring(ses.payload, &passwordlen);
  85. + /* We run the PAM conversation regardless of whether the username is valid
  86. + in case the conversation function has an inherent delay.
  87. + Use ses.authstate.username rather than ses.authstate.pw_name.
  88. + After PAM succeeds we then check the valid_user flag too */
  89. +
  90. /* used to pass data to the PAM conversation function - don't bother with
  91. * strdup() etc since these are touched only by our own conversation
  92. * function (above) which takes care of it */
  93. - userData.user = ses.authstate.pw_name;
  94. + userData.user = ses.authstate.username;
  95. userData.passwd = password;
  96. + if (ses.authstate.pw_name) {
  97. + printable_user = ses.authstate.pw_name;
  98. + } else {
  99. + printable_user = "<invalid username>";
  100. + }
  101. +
  102. /* Init pam */
  103. if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) {
  104. dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s",
  105. @@ -236,7 +248,7 @@ void svr_auth_pam() {
  106. rc, pam_strerror(pamHandlep, rc));
  107. dropbear_log(LOG_WARNING,
  108. "Bad PAM password attempt for '%s' from %s",
  109. - ses.authstate.pw_name,
  110. + printable_user,
  111. svr_ses.addrstring);
  112. send_msg_userauth_failure(0, 1);
  113. goto cleanup;
  114. @@ -247,12 +259,18 @@ void svr_auth_pam() {
  115. rc, pam_strerror(pamHandlep, rc));
  116. dropbear_log(LOG_WARNING,
  117. "Bad PAM password attempt for '%s' from %s",
  118. - ses.authstate.pw_name,
  119. + printable_user,
  120. svr_ses.addrstring);
  121. send_msg_userauth_failure(0, 1);
  122. goto cleanup;
  123. }
  124. + if (!valid_user) {
  125. + /* PAM auth succeeded but the username isn't allowed in for another reason
  126. + (checkusername() failed) */
  127. + send_msg_userauth_failure(0, 1);
  128. + }
  129. +
  130. /* successful authentication */
  131. dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s",
  132. ses.authstate.pw_name,
  133. --- a/svr-authpasswd.c
  134. +++ b/svr-authpasswd.c
  135. @@ -48,22 +48,14 @@ static int constant_time_strcmp(const ch
  136. /* Process a password auth request, sending success or failure messages as
  137. * appropriate */
  138. -void svr_auth_password() {
  139. +void svr_auth_password(int valid_user) {
  140. char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */
  141. char * testcrypt = NULL; /* crypt generated from the user's password sent */
  142. - char * password;
  143. + char * password = NULL;
  144. unsigned int passwordlen;
  145. -
  146. unsigned int changepw;
  147. - passwdcrypt = ses.authstate.pw_passwd;
  148. -
  149. -#ifdef DEBUG_HACKCRYPT
  150. - /* debugging crypt for non-root testing with shadows */
  151. - passwdcrypt = DEBUG_HACKCRYPT;
  152. -#endif
  153. -
  154. /* check if client wants to change password */
  155. changepw = buf_getbool(ses.payload);
  156. if (changepw) {
  157. @@ -73,12 +65,21 @@ void svr_auth_password() {
  158. }
  159. password = buf_getstring(ses.payload, &passwordlen);
  160. -
  161. - /* the first bytes of passwdcrypt are the salt */
  162. - testcrypt = crypt(password, passwdcrypt);
  163. + if (valid_user) {
  164. + /* the first bytes of passwdcrypt are the salt */
  165. + passwdcrypt = ses.authstate.pw_passwd;
  166. + testcrypt = crypt(password, passwdcrypt);
  167. + }
  168. m_burn(password, passwordlen);
  169. m_free(password);
  170. + /* After we have got the payload contents we can exit if the username
  171. + is invalid. Invalid users have already been logged. */
  172. + if (!valid_user) {
  173. + send_msg_userauth_failure(0, 1);
  174. + return;
  175. + }
  176. +
  177. if (testcrypt == NULL) {
  178. /* crypt() with an invalid salt like "!!" */
  179. dropbear_log(LOG_WARNING, "User account '%s' is locked",
  180. --- a/svr-authpubkey.c
  181. +++ b/svr-authpubkey.c
  182. @@ -79,7 +79,7 @@ static int checkfileperm(char * filename
  183. /* process a pubkey auth request, sending success or failure message as
  184. * appropriate */
  185. -void svr_auth_pubkey() {
  186. +void svr_auth_pubkey(int valid_user) {
  187. unsigned char testkey; /* whether we're just checking if a key is usable */
  188. char* algo = NULL; /* pubkey algo */
  189. @@ -102,6 +102,15 @@ void svr_auth_pubkey() {
  190. keybloblen = buf_getint(ses.payload);
  191. keyblob = buf_getptr(ses.payload, keybloblen);
  192. + if (!valid_user) {
  193. + /* Return failure once we have read the contents of the packet
  194. + required to validate a public key.
  195. + Avoids blind user enumeration though it isn't possible to prevent
  196. + testing for user existence if the public key is known */
  197. + send_msg_userauth_failure(0, 0);
  198. + goto out;
  199. + }
  200. +
  201. /* check if the key is valid */
  202. if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) {
  203. send_msg_userauth_failure(0, 0);