aboutsummaryrefslogtreecommitdiffstats
path: root/main/dropbear/CVE-2018-15599.patch
blob: 963ed35bb9fd23c03eb925090e021cbd95f1b544 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

# HG changeset patch
# User Matt Johnston <matt@ucc.asn.au>
# Date 1535038992 -28800
# Node ID 5d2d1021ca0006337af58859c861f2349715b73b
# Parent  cd23631dab5c653c66fe697e24f026aefbfcecb3
Wait to fail invalid usernames

diff -r cd23631dab5c -r 5d2d1021ca00 auth.h
--- a/auth.h	Thu Aug 23 22:10:07 2018 +0800
+++ b/auth.h	Thu Aug 23 23:43:12 2018 +0800
@@ -37,9 +37,9 @@
 void send_msg_userauth_failure(int partial, int incrfail);
 void send_msg_userauth_success(void);
 void send_msg_userauth_banner(const buffer *msg);
-void svr_auth_password(void);
-void svr_auth_pubkey(void);
-void svr_auth_pam(void);
+void svr_auth_password(int valid_user);
+void svr_auth_pubkey(int valid_user);
+void svr_auth_pam(int valid_user);
 
 #if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
 int svr_pubkey_allows_agentfwd(void);
diff -r cd23631dab5c -r 5d2d1021ca00 svr-auth.c
--- a/svr-auth.c	Thu Aug 23 22:10:07 2018 +0800
+++ b/svr-auth.c	Thu Aug 23 23:43:12 2018 +0800
@@ -149,10 +149,8 @@
 		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
 				strncmp(methodname, AUTH_METHOD_PASSWORD,
 					AUTH_METHOD_PASSWORD_LEN) == 0) {
-			if (valid_user) {
-				svr_auth_password();
-				goto out;
-			}
+			svr_auth_password(valid_user);
+			goto out;
 		}
 	}
 #endif
@@ -164,10 +162,8 @@
 		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
 				strncmp(methodname, AUTH_METHOD_PASSWORD,
 					AUTH_METHOD_PASSWORD_LEN) == 0) {
-			if (valid_user) {
-				svr_auth_pam();
-				goto out;
-			}
+			svr_auth_pam(valid_user);
+			goto out;
 		}
 	}
 #endif
@@ -177,12 +173,7 @@
 	if (methodlen == AUTH_METHOD_PUBKEY_LEN &&
 			strncmp(methodname, AUTH_METHOD_PUBKEY,
 				AUTH_METHOD_PUBKEY_LEN) == 0) {
-		if (valid_user) {
-			svr_auth_pubkey();
-		} else {
-			/* pubkey has no failure delay */
-			send_msg_userauth_failure(0, 0);
-		}
+		svr_auth_pubkey(valid_user);
 		goto out;
 	}
 #endif
diff -r cd23631dab5c -r 5d2d1021ca00 svr-authpam.c
--- a/svr-authpam.c	Thu Aug 23 22:10:07 2018 +0800
+++ b/svr-authpam.c	Thu Aug 23 23:43:12 2018 +0800
@@ -178,13 +178,14 @@
  * Keyboard interactive would be a lot nicer, but since PAM is synchronous, it
  * gets very messy trying to send the interactive challenges, and read the
  * interactive responses, over the network. */
-void svr_auth_pam() {
+void svr_auth_pam(int valid_user) {
 
 	struct UserDataS userData = {NULL, NULL};
 	struct pam_conv pamConv = {
 		pamConvFunc,
 		&userData /* submitted to pamvConvFunc as appdata_ptr */ 
 	};
+	const char* printable_user = NULL;
 
 	pam_handle_t* pamHandlep = NULL;
 
@@ -204,12 +205,23 @@
 
 	password = buf_getstring(ses.payload, &passwordlen);
 
+	/* We run the PAM conversation regardless of whether the username is valid
+	in case the conversation function has an inherent delay.
+	Use ses.authstate.username rather than ses.authstate.pw_name.
+	After PAM succeeds we then check the valid_user flag too */
+
 	/* used to pass data to the PAM conversation function - don't bother with
 	 * strdup() etc since these are touched only by our own conversation
 	 * function (above) which takes care of it */
-	userData.user = ses.authstate.pw_name;
+	userData.user = ses.authstate.username;
 	userData.passwd = password;
 
+	if (ses.authstate.pw_name) {
+		printable_user = ses.authstate.pw_name;
+	} else {
+		printable_user = "<invalid username>";
+	}
+
 	/* Init pam */
 	if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) {
 		dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s", 
@@ -242,7 +254,7 @@
 				rc, pam_strerror(pamHandlep, rc));
 		dropbear_log(LOG_WARNING,
 				"Bad PAM password attempt for '%s' from %s",
-				ses.authstate.pw_name,
+				printable_user,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 		goto cleanup;
@@ -253,12 +265,18 @@
 				rc, pam_strerror(pamHandlep, rc));
 		dropbear_log(LOG_WARNING,
 				"Bad PAM password attempt for '%s' from %s",
-				ses.authstate.pw_name,
+				printable_user,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 		goto cleanup;
 	}
 
+	if (!valid_user) {
+		/* PAM auth succeeded but the username isn't allowed in for another reason
+		(checkusername() failed) */
+		send_msg_userauth_failure(0, 1);
+	}
+
 	/* successful authentication */
 	dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s",
 			ses.authstate.pw_name,
diff -r cd23631dab5c -r 5d2d1021ca00 svr-authpasswd.c
--- a/svr-authpasswd.c	Thu Aug 23 22:10:07 2018 +0800
+++ b/svr-authpasswd.c	Thu Aug 23 23:43:12 2018 +0800
@@ -48,22 +48,14 @@
 
 /* Process a password auth request, sending success or failure messages as
  * appropriate */
-void svr_auth_password() {
+void svr_auth_password(int valid_user) {
 	
 	char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */
 	char * testcrypt = NULL; /* crypt generated from the user's password sent */
-	char * password;
+	char * password = NULL;
 	unsigned int passwordlen;
-
 	unsigned int changepw;
 
-	passwdcrypt = ses.authstate.pw_passwd;
-
-#ifdef DEBUG_HACKCRYPT
-	/* debugging crypt for non-root testing with shadows */
-	passwdcrypt = DEBUG_HACKCRYPT;
-#endif
-
 	/* check if client wants to change password */
 	changepw = buf_getbool(ses.payload);
 	if (changepw) {
@@ -73,12 +65,21 @@
 	}
 
 	password = buf_getstring(ses.payload, &passwordlen);
-
-	/* the first bytes of passwdcrypt are the salt */
-	testcrypt = crypt(password, passwdcrypt);
+	if (valid_user) {
+		/* the first bytes of passwdcrypt are the salt */
+		passwdcrypt = ses.authstate.pw_passwd;
+		testcrypt = crypt(password, passwdcrypt);
+	}
 	m_burn(password, passwordlen);
 	m_free(password);
 
+	/* After we have got the payload contents we can exit if the username
+	is invalid. Invalid users have already been logged. */
+	if (!valid_user) {
+		send_msg_userauth_failure(0, 1);
+		return;
+	}
+
 	if (testcrypt == NULL) {
 		/* crypt() with an invalid salt like "!!" */
 		dropbear_log(LOG_WARNING, "User account '%s' is locked",
diff -r cd23631dab5c -r 5d2d1021ca00 svr-authpubkey.c
--- a/svr-authpubkey.c	Thu Aug 23 22:10:07 2018 +0800
+++ b/svr-authpubkey.c	Thu Aug 23 23:43:12 2018 +0800
@@ -79,7 +79,7 @@
 
 /* process a pubkey auth request, sending success or failure message as
  * appropriate */
-void svr_auth_pubkey() {
+void svr_auth_pubkey(int valid_user) {
 
 	unsigned char testkey; /* whether we're just checking if a key is usable */
 	char* algo = NULL; /* pubkey algo */
@@ -102,6 +102,15 @@
 	keybloblen = buf_getint(ses.payload);
 	keyblob = buf_getptr(ses.payload, keybloblen);
 
+	if (!valid_user) {
+		/* Return failure once we have read the contents of the packet
+		required to validate a public key. 
+		Avoids blind user enumeration though it isn't possible to prevent
+		testing for user existence if the public key is known */
+		send_msg_userauth_failure(0, 0);
+		goto out;
+	}
+
 	/* check if the key is valid */
 	if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) {
 		send_msg_userauth_failure(0, 0);