xref: /freebsd/crypto/openssh/auth2-gss.c (revision 8e9740b62e950b40dcf7dbe729855be14450d40d)
1 /* $OpenBSD: auth2-gss.c,v 1.32 2021/01/27 10:15:08 djm Exp $ */
2 
3 /*
4  * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "includes.h"
28 
29 #ifdef GSSAPI
30 
31 #include <sys/types.h>
32 
33 #include <stdarg.h>
34 
35 #include "xmalloc.h"
36 #include "sshkey.h"
37 #include "hostfile.h"
38 #include "auth.h"
39 #include "ssh2.h"
40 #include "log.h"
41 #include "dispatch.h"
42 #include "sshbuf.h"
43 #include "ssherr.h"
44 #include "misc.h"
45 #include "servconf.h"
46 #include "packet.h"
47 #include "kex.h"
48 #include "ssh-gss.h"
49 #include "monitor_wrap.h"
50 
51 extern ServerOptions options;
52 
53 static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh);
54 static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
55 static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh);
56 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
57 
58 /*
59  * We only support those mechanisms that we know about (ie ones that we know
60  * how to check local user kuserok and the like)
61  */
62 static int
63 userauth_gssapi(struct ssh *ssh)
64 {
65 	Authctxt *authctxt = ssh->authctxt;
66 	gss_OID_desc goid = {0, NULL};
67 	Gssctxt *ctxt = NULL;
68 	int r, present;
69 	u_int mechs;
70 	OM_uint32 ms;
71 	size_t len;
72 	u_char *doid = NULL;
73 
74 	if ((r = sshpkt_get_u32(ssh, &mechs)) != 0)
75 		fatal_fr(r, "parse packet");
76 
77 	if (mechs == 0) {
78 		debug("Mechanism negotiation is not supported");
79 		return (0);
80 	}
81 
82 	do {
83 		mechs--;
84 
85 		free(doid);
86 
87 		present = 0;
88 		if ((r = sshpkt_get_string(ssh, &doid, &len)) != 0)
89 			fatal_fr(r, "parse oid");
90 
91 		if (len > 2 && doid[0] == SSH_GSS_OIDTYPE &&
92 		    doid[1] == len - 2) {
93 			goid.elements = doid + 2;
94 			goid.length   = len - 2;
95 			ssh_gssapi_test_oid_supported(&ms, &goid, &present);
96 		} else {
97 			logit("Badly formed OID received");
98 		}
99 	} while (mechs > 0 && !present);
100 
101 	if (!present) {
102 		free(doid);
103 		authctxt->server_caused_failure = 1;
104 		return (0);
105 	}
106 
107 	if (!authctxt->valid || authctxt->user == NULL) {
108 		debug2_f("disabled because of invalid user");
109 		free(doid);
110 		return (0);
111 	}
112 
113 	if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) {
114 		if (ctxt != NULL)
115 			ssh_gssapi_delete_ctx(&ctxt);
116 		free(doid);
117 		authctxt->server_caused_failure = 1;
118 		return (0);
119 	}
120 
121 	authctxt->methoddata = (void *)ctxt;
122 
123 	/* Return the OID that we received */
124 	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE)) != 0 ||
125 	    (r = sshpkt_put_string(ssh, doid, len)) != 0 ||
126 	    (r = sshpkt_send(ssh)) != 0)
127 		fatal_fr(r, "send packet");
128 
129 	free(doid);
130 
131 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
132 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
133 	authctxt->postponed = 1;
134 
135 	return (0);
136 }
137 
138 static int
139 input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh)
140 {
141 	Authctxt *authctxt = ssh->authctxt;
142 	Gssctxt *gssctxt;
143 	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
144 	gss_buffer_desc recv_tok;
145 	OM_uint32 maj_status, min_status, flags;
146 	u_char *p;
147 	size_t len;
148 	int r;
149 
150 	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
151 		fatal("No authentication or GSSAPI context");
152 
153 	gssctxt = authctxt->methoddata;
154 	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
155 	    (r = sshpkt_get_end(ssh)) != 0)
156 		fatal_fr(r, "parse packet");
157 
158 	recv_tok.value = p;
159 	recv_tok.length = len;
160 	maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
161 	    &send_tok, &flags));
162 
163 	free(p);
164 
165 	if (GSS_ERROR(maj_status)) {
166 		if (send_tok.length != 0) {
167 			if ((r = sshpkt_start(ssh,
168 			    SSH2_MSG_USERAUTH_GSSAPI_ERRTOK)) != 0 ||
169 			    (r = sshpkt_put_string(ssh, send_tok.value,
170 			    send_tok.length)) != 0 ||
171 			    (r = sshpkt_send(ssh)) != 0)
172 				fatal_fr(r, "send ERRTOK packet");
173 		}
174 		authctxt->postponed = 0;
175 		ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
176 		userauth_finish(ssh, 0, "gssapi-with-mic", NULL);
177 	} else {
178 		if (send_tok.length != 0) {
179 			if ((r = sshpkt_start(ssh,
180 			    SSH2_MSG_USERAUTH_GSSAPI_TOKEN)) != 0 ||
181 			    (r = sshpkt_put_string(ssh, send_tok.value,
182 			    send_tok.length)) != 0 ||
183 			    (r = sshpkt_send(ssh)) != 0)
184 				fatal_fr(r, "send TOKEN packet");
185 		}
186 		if (maj_status == GSS_S_COMPLETE) {
187 			ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
188 			if (flags & GSS_C_INTEG_FLAG)
189 				ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC,
190 				    &input_gssapi_mic);
191 			else
192 				ssh_dispatch_set(ssh,
193 				    SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
194 				    &input_gssapi_exchange_complete);
195 		}
196 	}
197 
198 	gss_release_buffer(&min_status, &send_tok);
199 	return 0;
200 }
201 
202 static int
203 input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh)
204 {
205 	Authctxt *authctxt = ssh->authctxt;
206 	Gssctxt *gssctxt;
207 	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
208 	gss_buffer_desc recv_tok;
209 	OM_uint32 maj_status;
210 	int r;
211 	u_char *p;
212 	size_t len;
213 
214 	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
215 		fatal("No authentication or GSSAPI context");
216 
217 	gssctxt = authctxt->methoddata;
218 	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
219 	    (r = sshpkt_get_end(ssh)) != 0)
220 		fatal_fr(r, "parse packet");
221 	recv_tok.value = p;
222 	recv_tok.length = len;
223 
224 	/* Push the error token into GSSAPI to see what it says */
225 	maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
226 	    &send_tok, NULL));
227 
228 	free(recv_tok.value);
229 
230 	/* We can't return anything to the client, even if we wanted to */
231 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
232 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
233 
234 	/* The client will have already moved on to the next auth */
235 
236 	gss_release_buffer(&maj_status, &send_tok);
237 	return 0;
238 }
239 
240 /*
241  * This is called when the client thinks we've completed authentication.
242  * It should only be enabled in the dispatch handler by the function above,
243  * which only enables it once the GSSAPI exchange is complete.
244  */
245 
246 static int
247 input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
248 {
249 	Authctxt *authctxt = ssh->authctxt;
250 	int r, authenticated;
251 	const char *displayname;
252 
253 	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
254 		fatal("No authentication or GSSAPI context");
255 
256 	/*
257 	 * We don't need to check the status, because we're only enabled in
258 	 * the dispatcher once the exchange is complete
259 	 */
260 
261 	if ((r = sshpkt_get_end(ssh)) != 0)
262 		fatal_fr(r, "parse packet");
263 
264 	authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
265 
266 	if ((!use_privsep || mm_is_monitor()) &&
267 	    (displayname = ssh_gssapi_displayname()) != NULL)
268 		auth2_record_info(authctxt, "%s", displayname);
269 
270 	authctxt->postponed = 0;
271 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
272 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
273 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
274 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
275 	userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL);
276 	return 0;
277 }
278 
279 static int
280 input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
281 {
282 	Authctxt *authctxt = ssh->authctxt;
283 	Gssctxt *gssctxt;
284 	int r, authenticated = 0;
285 	struct sshbuf *b;
286 	gss_buffer_desc mic, gssbuf;
287 	const char *displayname;
288 	u_char *p;
289 	size_t len;
290 
291 	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
292 		fatal("No authentication or GSSAPI context");
293 
294 	gssctxt = authctxt->methoddata;
295 
296 	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0)
297 		fatal_fr(r, "parse packet");
298 	if ((b = sshbuf_new()) == NULL)
299 		fatal_f("sshbuf_new failed");
300 	mic.value = p;
301 	mic.length = len;
302 	ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
303 	    "gssapi-with-mic", ssh->kex->session_id);
304 
305 	if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
306 		fatal_f("sshbuf_mutable_ptr failed");
307 	gssbuf.length = sshbuf_len(b);
308 
309 	if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
310 		authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
311 	else
312 		logit("GSSAPI MIC check failed");
313 
314 	sshbuf_free(b);
315 	free(mic.value);
316 
317 	if ((!use_privsep || mm_is_monitor()) &&
318 	    (displayname = ssh_gssapi_displayname()) != NULL)
319 		auth2_record_info(authctxt, "%s", displayname);
320 
321 	authctxt->postponed = 0;
322 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
323 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
324 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
325 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
326 	userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL);
327 	return 0;
328 }
329 
330 Authmethod method_gssapi = {
331 	"gssapi-with-mic",
332 	userauth_gssapi,
333 	&options.gss_authentication
334 };
335 
336 #endif /* GSSAPI */
337