xref: /freebsd/crypto/openssh/auth2-pubkey.c (revision f0adf7f5cdd241db2f2c817683191a6ef64a4e95)
1 /*
2  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 
25 #include "includes.h"
26 RCSID("$OpenBSD: auth2-pubkey.c,v 1.6 2004/01/19 21:25:15 markus Exp $");
27 
28 #include "ssh2.h"
29 #include "xmalloc.h"
30 #include "packet.h"
31 #include "buffer.h"
32 #include "log.h"
33 #include "servconf.h"
34 #include "compat.h"
35 #include "bufaux.h"
36 #include "auth.h"
37 #include "key.h"
38 #include "pathnames.h"
39 #include "uidswap.h"
40 #include "auth-options.h"
41 #include "canohost.h"
42 #include "monitor_wrap.h"
43 
44 /* import */
45 extern ServerOptions options;
46 extern u_char *session_id2;
47 extern u_int session_id2_len;
48 
49 static int
50 userauth_pubkey(Authctxt *authctxt)
51 {
52 	Buffer b;
53 	Key *key = NULL;
54 	char *pkalg;
55 	u_char *pkblob, *sig;
56 	u_int alen, blen, slen;
57 	int have_sig, pktype;
58 	int authenticated = 0;
59 
60 	if (!authctxt->valid) {
61 		debug2("userauth_pubkey: disabled because of invalid user");
62 		return 0;
63 	}
64 	have_sig = packet_get_char();
65 	if (datafellows & SSH_BUG_PKAUTH) {
66 		debug2("userauth_pubkey: SSH_BUG_PKAUTH");
67 		/* no explicit pkalg given */
68 		pkblob = packet_get_string(&blen);
69 		buffer_init(&b);
70 		buffer_append(&b, pkblob, blen);
71 		/* so we have to extract the pkalg from the pkblob */
72 		pkalg = buffer_get_string(&b, &alen);
73 		buffer_free(&b);
74 	} else {
75 		pkalg = packet_get_string(&alen);
76 		pkblob = packet_get_string(&blen);
77 	}
78 	pktype = key_type_from_name(pkalg);
79 	if (pktype == KEY_UNSPEC) {
80 		/* this is perfectly legal */
81 		logit("userauth_pubkey: unsupported public key algorithm: %s",
82 		    pkalg);
83 		goto done;
84 	}
85 	key = key_from_blob(pkblob, blen);
86 	if (key == NULL) {
87 		error("userauth_pubkey: cannot decode key: %s", pkalg);
88 		goto done;
89 	}
90 	if (key->type != pktype) {
91 		error("userauth_pubkey: type mismatch for decoded key "
92 		    "(received %d, expected %d)", key->type, pktype);
93 		goto done;
94 	}
95 	if (have_sig) {
96 		sig = packet_get_string(&slen);
97 		packet_check_eom();
98 		buffer_init(&b);
99 		if (datafellows & SSH_OLD_SESSIONID) {
100 			buffer_append(&b, session_id2, session_id2_len);
101 		} else {
102 			buffer_put_string(&b, session_id2, session_id2_len);
103 		}
104 		/* reconstruct packet */
105 		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
106 		buffer_put_cstring(&b, authctxt->user);
107 		buffer_put_cstring(&b,
108 		    datafellows & SSH_BUG_PKSERVICE ?
109 		    "ssh-userauth" :
110 		    authctxt->service);
111 		if (datafellows & SSH_BUG_PKAUTH) {
112 			buffer_put_char(&b, have_sig);
113 		} else {
114 			buffer_put_cstring(&b, "publickey");
115 			buffer_put_char(&b, have_sig);
116 			buffer_put_cstring(&b, pkalg);
117 		}
118 		buffer_put_string(&b, pkblob, blen);
119 #ifdef DEBUG_PK
120 		buffer_dump(&b);
121 #endif
122 		/* test for correct signature */
123 		authenticated = 0;
124 		if (PRIVSEP(user_key_allowed(authctxt->pw, key)) &&
125 		    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
126 		    buffer_len(&b))) == 1)
127 			authenticated = 1;
128 		buffer_free(&b);
129 		xfree(sig);
130 	} else {
131 		debug("test whether pkalg/pkblob are acceptable");
132 		packet_check_eom();
133 
134 		/* XXX fake reply and always send PK_OK ? */
135 		/*
136 		 * XXX this allows testing whether a user is allowed
137 		 * to login: if you happen to have a valid pubkey this
138 		 * message is sent. the message is NEVER sent at all
139 		 * if a user is not allowed to login. is this an
140 		 * issue? -markus
141 		 */
142 		if (PRIVSEP(user_key_allowed(authctxt->pw, key))) {
143 			packet_start(SSH2_MSG_USERAUTH_PK_OK);
144 			packet_put_string(pkalg, alen);
145 			packet_put_string(pkblob, blen);
146 			packet_send();
147 			packet_write_wait();
148 			authctxt->postponed = 1;
149 		}
150 	}
151 	if (authenticated != 1)
152 		auth_clear_options();
153 done:
154 	debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
155 	if (key != NULL)
156 		key_free(key);
157 	xfree(pkalg);
158 	xfree(pkblob);
159 #ifdef HAVE_CYGWIN
160 	if (check_nt_auth(0, authctxt->pw) == 0)
161 		return(0);
162 #endif
163 	return authenticated;
164 }
165 
166 /* return 1 if user allows given key */
167 static int
168 user_key_allowed2(struct passwd *pw, Key *key, char *file)
169 {
170 	char line[8192];
171 	int found_key = 0;
172 	FILE *f;
173 	u_long linenum = 0;
174 	struct stat st;
175 	Key *found;
176 	char *fp;
177 
178 	/* Temporarily use the user's uid. */
179 	temporarily_use_uid(pw);
180 
181 	debug("trying public key file %s", file);
182 
183 	/* Fail quietly if file does not exist */
184 	if (stat(file, &st) < 0) {
185 		/* Restore the privileged uid. */
186 		restore_uid();
187 		return 0;
188 	}
189 	/* Open the file containing the authorized keys. */
190 	f = fopen(file, "r");
191 	if (!f) {
192 		/* Restore the privileged uid. */
193 		restore_uid();
194 		return 0;
195 	}
196 	if (options.strict_modes &&
197 	    secure_filename(f, file, pw, line, sizeof(line)) != 0) {
198 		fclose(f);
199 		logit("Authentication refused: %s", line);
200 		restore_uid();
201 		return 0;
202 	}
203 
204 	found_key = 0;
205 	found = key_new(key->type);
206 
207 	while (fgets(line, sizeof(line), f)) {
208 		char *cp, *options = NULL;
209 		linenum++;
210 		/* Skip leading whitespace, empty and comment lines. */
211 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
212 			;
213 		if (!*cp || *cp == '\n' || *cp == '#')
214 			continue;
215 
216 		if (key_read(found, &cp) != 1) {
217 			/* no key?  check if there are options for this key */
218 			int quoted = 0;
219 			debug2("user_key_allowed: check options: '%s'", cp);
220 			options = cp;
221 			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
222 				if (*cp == '\\' && cp[1] == '"')
223 					cp++;	/* Skip both */
224 				else if (*cp == '"')
225 					quoted = !quoted;
226 			}
227 			/* Skip remaining whitespace. */
228 			for (; *cp == ' ' || *cp == '\t'; cp++)
229 				;
230 			if (key_read(found, &cp) != 1) {
231 				debug2("user_key_allowed: advance: '%s'", cp);
232 				/* still no key?  advance to next line*/
233 				continue;
234 			}
235 		}
236 		if (key_equal(found, key) &&
237 		    auth_parse_options(pw, options, file, linenum) == 1) {
238 			found_key = 1;
239 			debug("matching key found: file %s, line %lu",
240 			    file, linenum);
241 			fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
242 			verbose("Found matching %s key: %s",
243 			    key_type(found), fp);
244 			xfree(fp);
245 			break;
246 		}
247 	}
248 	restore_uid();
249 	fclose(f);
250 	key_free(found);
251 	if (!found_key)
252 		debug2("key not found");
253 	return found_key;
254 }
255 
256 /* check whether given key is in .ssh/authorized_keys* */
257 int
258 user_key_allowed(struct passwd *pw, Key *key)
259 {
260 	int success;
261 	char *file;
262 
263 	file = authorized_keys_file(pw);
264 	success = user_key_allowed2(pw, key, file);
265 	xfree(file);
266 	if (success)
267 		return success;
268 
269 	/* try suffix "2" for backward compat, too */
270 	file = authorized_keys_file2(pw);
271 	success = user_key_allowed2(pw, key, file);
272 	xfree(file);
273 	return success;
274 }
275 
276 Authmethod method_pubkey = {
277 	"publickey",
278 	userauth_pubkey,
279 	&options.pubkey_authentication
280 };
281