xref: /dragonfly/crypto/openssh/auth2-pubkey.c (revision 984263bc)
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.2 2002/05/31 11:35: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 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 		log("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_clear(&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 	if (pw == NULL)
179 		return 0;
180 
181 	/* Temporarily use the user's uid. */
182 	temporarily_use_uid(pw);
183 
184 	debug("trying public key file %s", file);
185 
186 	/* Fail quietly if file does not exist */
187 	if (stat(file, &st) < 0) {
188 		/* Restore the privileged uid. */
189 		restore_uid();
190 		return 0;
191 	}
192 	/* Open the file containing the authorized keys. */
193 	f = fopen(file, "r");
194 	if (!f) {
195 		/* Restore the privileged uid. */
196 		restore_uid();
197 		return 0;
198 	}
199 	if (options.strict_modes &&
200 	    secure_filename(f, file, pw, line, sizeof(line)) != 0) {
201 		fclose(f);
202 		log("Authentication refused: %s", line);
203 		restore_uid();
204 		return 0;
205 	}
206 
207 	found_key = 0;
208 	found = key_new(key->type);
209 
210 	while (fgets(line, sizeof(line), f)) {
211 		char *cp, *options = NULL;
212 		linenum++;
213 		/* Skip leading whitespace, empty and comment lines. */
214 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
215 			;
216 		if (!*cp || *cp == '\n' || *cp == '#')
217 			continue;
218 
219 		if (key_read(found, &cp) != 1) {
220 			/* no key?  check if there are options for this key */
221 			int quoted = 0;
222 			debug2("user_key_allowed: check options: '%s'", cp);
223 			options = cp;
224 			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
225 				if (*cp == '\\' && cp[1] == '"')
226 					cp++;	/* Skip both */
227 				else if (*cp == '"')
228 					quoted = !quoted;
229 			}
230 			/* Skip remaining whitespace. */
231 			for (; *cp == ' ' || *cp == '\t'; cp++)
232 				;
233 			if (key_read(found, &cp) != 1) {
234 				debug2("user_key_allowed: advance: '%s'", cp);
235 				/* still no key?  advance to next line*/
236 				continue;
237 			}
238 		}
239 		if (key_equal(found, key) &&
240 		    auth_parse_options(pw, options, file, linenum) == 1) {
241 			found_key = 1;
242 			debug("matching key found: file %s, line %lu",
243 			    file, linenum);
244 			fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
245 			verbose("Found matching %s key: %s",
246 			    key_type(found), fp);
247 			xfree(fp);
248 			break;
249 		}
250 	}
251 	restore_uid();
252 	fclose(f);
253 	key_free(found);
254 	if (!found_key)
255 		debug2("key not found");
256 	return found_key;
257 }
258 
259 /* check whether given key is in .ssh/authorized_keys* */
260 int
261 user_key_allowed(struct passwd *pw, Key *key)
262 {
263 	int success;
264 	char *file;
265 
266 	file = authorized_keys_file(pw);
267 	success = user_key_allowed2(pw, key, file);
268 	xfree(file);
269 	if (success)
270 		return success;
271 
272 	/* try suffix "2" for backward compat, too */
273 	file = authorized_keys_file2(pw);
274 	success = user_key_allowed2(pw, key, file);
275 	xfree(file);
276 	return success;
277 }
278 
279 Authmethod method_pubkey = {
280 	"publickey",
281 	userauth_pubkey,
282 	&options.pubkey_authentication
283 };
284