• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Dropbear - a SSH2 server
3  *
4  * Copyright (c) 2002,2003 Matt Johnston
5  * All rights reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE. */
24 
25 /* Process a pubkey auth request */
26 
27 #include "includes.h"
28 #include "session.h"
29 #include "dbutil.h"
30 #include "buffer.h"
31 #include "signkey.h"
32 #include "auth.h"
33 #include "ssh.h"
34 #include "packet.h"
35 #include "algo.h"
36 
37 #ifdef ENABLE_SVR_PUBKEY_AUTH
38 
39 #define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */
40 #define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */
41 
42 static int checkpubkey(unsigned char* algo, unsigned int algolen,
43 		unsigned char* keyblob, unsigned int keybloblen);
44 static int checkpubkeyperms();
45 static void send_msg_userauth_pk_ok(unsigned char* algo, unsigned int algolen,
46 		unsigned char* keyblob, unsigned int keybloblen);
47 static int checkfileperm(char * filename);
48 
49 /* process a pubkey auth request, sending success or failure message as
50  * appropriate */
svr_auth_pubkey()51 void svr_auth_pubkey() {
52 
53 	unsigned char testkey; /* whether we're just checking if a key is usable */
54 	unsigned char* algo = NULL; /* pubkey algo */
55 	unsigned int algolen;
56 	unsigned char* keyblob = NULL;
57 	unsigned int keybloblen;
58 	buffer * signbuf = NULL;
59 	sign_key * key = NULL;
60 	char* fp = NULL;
61 	int type = -1;
62 
63 	TRACE(("enter pubkeyauth"))
64 
65 	/* 0 indicates user just wants to check if key can be used, 1 is an
66 	 * actual attempt*/
67 	testkey = (buf_getbool(ses.payload) == 0);
68 
69 	algo = buf_getstring(ses.payload, &algolen);
70 	keybloblen = buf_getint(ses.payload);
71 	keyblob = buf_getptr(ses.payload, keybloblen);
72 
73 	/* check if the key is valid */
74 	if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) {
75 		send_msg_userauth_failure(0, 0);
76 		goto out;
77 	}
78 
79 	/* let them know that the key is ok to use */
80 	if (testkey) {
81 		send_msg_userauth_pk_ok(algo, algolen, keyblob, keybloblen);
82 		goto out;
83 	}
84 
85 	/* now we can actually verify the signature */
86 
87 	/* get the key */
88 	key = new_sign_key();
89 	type = DROPBEAR_SIGNKEY_ANY;
90 	if (buf_get_pub_key(ses.payload, key, &type) == DROPBEAR_FAILURE) {
91 		send_msg_userauth_failure(0, 1);
92 		goto out;
93 	}
94 
95 	/* create the data which has been signed - this a string containing
96 	 * session_id, concatenated with the payload packet up to the signature */
97 	signbuf = buf_new(ses.payload->pos + 4 + SHA1_HASH_SIZE);
98 	buf_putstring(signbuf, ses.session_id, SHA1_HASH_SIZE);
99 	buf_putbytes(signbuf, ses.payload->data, ses.payload->pos);
100 	buf_setpos(signbuf, 0);
101 
102 	/* ... and finally verify the signature */
103 	fp = sign_key_fingerprint(keyblob, keybloblen);
104 	if (buf_verify(ses.payload, key, buf_getptr(signbuf, signbuf->len),
105 				signbuf->len) == DROPBEAR_SUCCESS) {
106 		dropbear_log(LOG_NOTICE,
107 				"pubkey auth succeeded for '%s' with key %s from %s",
108 				ses.authstate.printableuser, fp, svr_ses.addrstring);
109 		send_msg_userauth_success();
110 	} else {
111 		dropbear_log(LOG_WARNING,
112 				"pubkey auth bad signature for '%s' with key %s from %s",
113 				ses.authstate.printableuser, fp, svr_ses.addrstring);
114 		send_msg_userauth_failure(0, 1);
115 	}
116 	m_free(fp);
117 
118 out:
119 	/* cleanup stuff */
120 	if (signbuf) {
121 		buf_free(signbuf);
122 	}
123 	if (algo) {
124 		m_free(algo);
125 	}
126 	if (key) {
127 		sign_key_free(key);
128 		key = NULL;
129 	}
130 	TRACE(("leave pubkeyauth"))
131 }
132 
133 /* Reply that the key is valid for auth, this is sent when the user sends
134  * a straight copy of their pubkey to test, to avoid having to perform
135  * expensive signing operations with a worthless key */
send_msg_userauth_pk_ok(unsigned char * algo,unsigned int algolen,unsigned char * keyblob,unsigned int keybloblen)136 static void send_msg_userauth_pk_ok(unsigned char* algo, unsigned int algolen,
137 		unsigned char* keyblob, unsigned int keybloblen) {
138 
139 	TRACE(("enter send_msg_userauth_pk_ok"))
140 	CHECKCLEARTOWRITE();
141 
142 	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PK_OK);
143 	buf_putstring(ses.writepayload, algo, algolen);
144 	buf_putstring(ses.writepayload, keyblob, keybloblen);
145 
146 	encrypt_packet();
147 	TRACE(("leave send_msg_userauth_pk_ok"))
148 
149 }
150 
151 /* Checks whether a specified publickey (and associated algorithm) is an
152  * acceptable key for authentication */
153 /* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
checkpubkey(unsigned char * algo,unsigned int algolen,unsigned char * keyblob,unsigned int keybloblen)154 static int checkpubkey(unsigned char* algo, unsigned int algolen,
155 		unsigned char* keyblob, unsigned int keybloblen) {
156 
157 	FILE * authfile = NULL;
158 	char * filename = NULL;
159 	int ret = DROPBEAR_FAILURE;
160 	buffer * line = NULL;
161 	unsigned int len, pos;
162 
163 	TRACE(("enter checkpubkey"))
164 
165 	/* check that we can use the algo */
166 	if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) {
167 		dropbear_log(LOG_WARNING,
168 				"pubkey auth attempt with unknown algo for '%s' from %s",
169 				ses.authstate.printableuser, svr_ses.addrstring);
170 		goto out;
171 	}
172 
173 	/* check file permissions, also whether file exists */
174 	if (checkpubkeyperms() == DROPBEAR_FAILURE) {
175 		TRACE(("bad authorized_keys permissions, or file doesn't exist"))
176 		goto out;
177 	}
178 
179 	/* we don't need to check pw and pw_dir for validity, since
180 	 * its been done in checkpubkeyperms. */
181 	len = strlen(ses.authstate.pw->pw_dir);
182 	/* allocate max required pathname storage,
183 	 * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
184 	filename = m_malloc(len + 22);
185 	snprintf(filename, len + 22, "%s/.ssh/authorized_keys",
186 				ses.authstate.pw->pw_dir);
187 
188 	/* open the file */
189 	authfile = fopen(filename, "r");
190 	if (authfile == NULL) {
191 		goto out;
192 	}
193 	TRACE(("checkpubkey: opened authorized_keys OK"))
194 
195 	line = buf_new(MAX_AUTHKEYS_LINE);
196 
197 	/* iterate through the lines */
198 	do {
199 
200 		if (buf_getline(line, authfile) == DROPBEAR_FAILURE) {
201 			/* EOF reached */
202 			TRACE(("checkpubkey: authorized_keys EOF reached"))
203 			break;
204 		}
205 
206 		if (line->len < MIN_AUTHKEYS_LINE) {
207 			TRACE(("checkpubkey: line too short"))
208 			continue; /* line is too short for it to be a valid key */
209 		}
210 
211 		/* check the key type - this also stops us from using keys
212 		 * which have options with them */
213 		if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) {
214 			continue;
215 		}
216 		buf_incrpos(line, algolen);
217 
218 		/* check for space (' ') character */
219 		if (buf_getbyte(line) != ' ') {
220 			TRACE(("checkpubkey: space character expected, isn't there"))
221 			continue;
222 		}
223 
224 		/* truncate the line at the space after the base64 data */
225 		pos = line->pos;
226 		for (len = 0; line->pos < line->len; len++) {
227 			if (buf_getbyte(line) == ' ') break;
228 		}
229 		buf_setpos(line, pos);
230 		buf_setlen(line, line->pos + len);
231 
232 		TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len))
233 
234 		ret = cmp_base64_key(keyblob, keybloblen, algo, algolen, line, NULL);
235 		if (ret == DROPBEAR_SUCCESS) {
236 			break;
237 		}
238 
239 		/* We continue to the next line otherwise */
240 
241 	} while (1);
242 
243 out:
244 	if (authfile) {
245 		fclose(authfile);
246 	}
247 	if (line) {
248 		buf_free(line);
249 	}
250 	m_free(filename);
251 	TRACE(("leave checkpubkey: ret=%d", ret))
252 	return ret;
253 }
254 
255 
256 /* Returns DROPBEAR_SUCCESS if file permissions for pubkeys are ok,
257  * DROPBEAR_FAILURE otherwise.
258  * Checks that the user's homedir, ~/.ssh, and
259  * ~/.ssh/authorized_keys are all owned by either root or the user, and are
260  * g-w, o-w */
checkpubkeyperms()261 static int checkpubkeyperms() {
262 
263 	char* filename = NULL;
264 	int ret = DROPBEAR_FAILURE;
265 	unsigned int len;
266 
267 	TRACE(("enter checkpubkeyperms"))
268 
269 	if (ses.authstate.pw->pw_dir == NULL) {
270 		goto out;
271 	}
272 
273 	if ((len = strlen(ses.authstate.pw->pw_dir)) == 0) {
274 		goto out;
275 	}
276 
277 	/* allocate max required pathname storage,
278 	 * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
279 	filename = m_malloc(len + 22);
280 	strncpy(filename, ses.authstate.pw->pw_dir, len+1);
281 
282 	/* check ~ */
283 	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
284 		goto out;
285 	}
286 
287 	/* check ~/.ssh */
288 	strncat(filename, "/.ssh", 5); /* strlen("/.ssh") == 5 */
289 	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
290 		goto out;
291 	}
292 
293 	/* now check ~/.ssh/authorized_keys */
294 	strncat(filename, "/authorized_keys", 16);
295 	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
296 		goto out;
297 	}
298 
299 	/* file looks ok, return success */
300 	ret = DROPBEAR_SUCCESS;
301 
302 out:
303 	m_free(filename);
304 
305 	TRACE(("leave checkpubkeyperms"))
306 	return ret;
307 }
308 
309 /* Checks that a file is owned by the user or root, and isn't writable by
310  * group or other */
311 /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
checkfileperm(char * filename)312 static int checkfileperm(char * filename) {
313 	struct stat filestat;
314 	int badperm = 0;
315 
316 	TRACE(("enter checkfileperm(%s)", filename))
317 
318 	if (stat(filename, &filestat) != 0) {
319 		TRACE(("leave checkfileperm: stat() != 0"))
320 		return DROPBEAR_FAILURE;
321 	}
322 	/* check ownership - user or root only*/
323 	if (filestat.st_uid != ses.authstate.pw->pw_uid
324 			&& filestat.st_uid != 0) {
325 		badperm = 1;
326 		TRACE(("wrong ownership"))
327 	}
328 	/* check permissions - don't want group or others +w */
329 	if (filestat.st_mode & (S_IWGRP | S_IWOTH)) {
330 		badperm = 1;
331 		TRACE(("wrong perms"))
332 	}
333 	if (badperm) {
334 		if (!ses.authstate.perm_warn) {
335 			ses.authstate.perm_warn = 1;
336 			dropbear_log(LOG_INFO, "%s must be owned by user or root, and not writable by others", filename);
337 		}
338 		TRACE(("leave checkfileperm: failure perms/owner"))
339 		return DROPBEAR_FAILURE;
340 	}
341 
342 	TRACE(("leave checkfileperm: success"))
343 	return DROPBEAR_SUCCESS;
344 }
345 
346 
347 #endif
348