auth2-gss.c revision 1.35
1/* $OpenBSD: auth2-gss.c,v 1.35 2024/05/17 00:30:23 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#ifdef GSSAPI
28
29#include <sys/types.h>
30
31#include "xmalloc.h"
32#include "sshkey.h"
33#include "hostfile.h"
34#include "auth.h"
35#include "ssh2.h"
36#include "log.h"
37#include "dispatch.h"
38#include "sshbuf.h"
39#include "ssherr.h"
40#include "servconf.h"
41#include "packet.h"
42#include "kex.h"
43#include "ssh-gss.h"
44#include "monitor_wrap.h"
45
46#define SSH_GSSAPI_MAX_MECHS	2048
47
48extern ServerOptions options;
49extern struct authmethod_cfg methodcfg_gssapi;
50
51static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh);
52static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
53static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh);
54static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
55
56/*
57 * We only support those mechanisms that we know about (ie ones that we know
58 * how to check local user kuserok and the like)
59 */
60static int
61userauth_gssapi(struct ssh *ssh, const char *method)
62{
63	Authctxt *authctxt = ssh->authctxt;
64	gss_OID_desc goid = {0, NULL};
65	Gssctxt *ctxt = NULL;
66	int r, present;
67	u_int mechs;
68	OM_uint32 ms;
69	size_t len;
70	u_char *doid = NULL;
71
72	if ((r = sshpkt_get_u32(ssh, &mechs)) != 0)
73		fatal_fr(r, "parse packet");
74
75	if (mechs == 0) {
76		logit_f("mechanism negotiation is not supported");
77		return (0);
78	} else if (mechs > SSH_GSSAPI_MAX_MECHS) {
79		logit_f("too many mechanisms requested %u > %u", mechs,
80		    SSH_GSSAPI_MAX_MECHS);
81		return (0);
82	}
83
84	do {
85		mechs--;
86
87		free(doid);
88
89		present = 0;
90		if ((r = sshpkt_get_string(ssh, &doid, &len)) != 0)
91			fatal_fr(r, "parse oid");
92
93		if (len > 2 && doid[0] == SSH_GSS_OIDTYPE &&
94		    doid[1] == len - 2) {
95			goid.elements = doid + 2;
96			goid.length   = len - 2;
97			ssh_gssapi_test_oid_supported(&ms, &goid, &present);
98		} else {
99			logit_f("badly formed OID received");
100		}
101	} while (mechs > 0 && !present);
102
103	if (!present) {
104		free(doid);
105		authctxt->server_caused_failure = 1;
106		return (0);
107	}
108
109	if (!authctxt->valid || authctxt->user == NULL) {
110		debug2_f("disabled because of invalid user");
111		free(doid);
112		return (0);
113	}
114
115	if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctxt, &goid))) {
116		if (ctxt != NULL)
117			ssh_gssapi_delete_ctx(&ctxt);
118		free(doid);
119		authctxt->server_caused_failure = 1;
120		return (0);
121	}
122
123	authctxt->methoddata = (void *)ctxt;
124
125	/* Return the OID that we received */
126	if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE)) != 0 ||
127	    (r = sshpkt_put_string(ssh, doid, len)) != 0 ||
128	    (r = sshpkt_send(ssh)) != 0)
129		fatal_fr(r, "send packet");
130
131	free(doid);
132
133	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
134	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
135	authctxt->postponed = 1;
136
137	return (0);
138}
139
140static int
141input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh)
142{
143	Authctxt *authctxt = ssh->authctxt;
144	Gssctxt *gssctxt;
145	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
146	gss_buffer_desc recv_tok;
147	OM_uint32 maj_status, min_status, flags;
148	u_char *p;
149	size_t len;
150	int r;
151
152	if (authctxt == NULL)
153		fatal("No authentication or GSSAPI context");
154
155	gssctxt = authctxt->methoddata;
156	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
157	    (r = sshpkt_get_end(ssh)) != 0)
158		fatal_fr(r, "parse packet");
159
160	recv_tok.value = p;
161	recv_tok.length = len;
162	maj_status = mm_ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
163	    &send_tok, &flags);
164
165	free(p);
166
167	if (GSS_ERROR(maj_status)) {
168		if (send_tok.length != 0) {
169			if ((r = sshpkt_start(ssh,
170			    SSH2_MSG_USERAUTH_GSSAPI_ERRTOK)) != 0 ||
171			    (r = sshpkt_put_string(ssh, send_tok.value,
172			    send_tok.length)) != 0 ||
173			    (r = sshpkt_send(ssh)) != 0)
174				fatal_fr(r, "send ERRTOK packet");
175		}
176		authctxt->postponed = 0;
177		ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
178		userauth_finish(ssh, 0, "gssapi-with-mic", NULL);
179	} else {
180		if (send_tok.length != 0) {
181			if ((r = sshpkt_start(ssh,
182			    SSH2_MSG_USERAUTH_GSSAPI_TOKEN)) != 0 ||
183			    (r = sshpkt_put_string(ssh, send_tok.value,
184			    send_tok.length)) != 0 ||
185			    (r = sshpkt_send(ssh)) != 0)
186				fatal_fr(r, "send TOKEN packet");
187		}
188		if (maj_status == GSS_S_COMPLETE) {
189			ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
190			if (flags & GSS_C_INTEG_FLAG)
191				ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC,
192				    &input_gssapi_mic);
193			else
194				ssh_dispatch_set(ssh,
195				    SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
196				    &input_gssapi_exchange_complete);
197		}
198	}
199
200	gss_release_buffer(&min_status, &send_tok);
201	return 0;
202}
203
204static int
205input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh)
206{
207	Authctxt *authctxt = ssh->authctxt;
208	Gssctxt *gssctxt;
209	gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
210	gss_buffer_desc recv_tok;
211	OM_uint32 maj_status;
212	int r;
213	u_char *p;
214	size_t len;
215
216	if (authctxt == NULL)
217		fatal("No authentication or GSSAPI context");
218
219	gssctxt = authctxt->methoddata;
220	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
221	    (r = sshpkt_get_end(ssh)) != 0)
222		fatal_fr(r, "parse packet");
223	recv_tok.value = p;
224	recv_tok.length = len;
225
226	/* Push the error token into GSSAPI to see what it says */
227	maj_status = mm_ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
228	    &send_tok, NULL);
229
230	free(recv_tok.value);
231
232	/* We can't return anything to the client, even if we wanted to */
233	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
234	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
235
236	/* The client will have already moved on to the next auth */
237
238	gss_release_buffer(&maj_status, &send_tok);
239	return 0;
240}
241
242/*
243 * This is called when the client thinks we've completed authentication.
244 * It should only be enabled in the dispatch handler by the function above,
245 * which only enables it once the GSSAPI exchange is complete.
246 */
247
248static int
249input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
250{
251	Authctxt *authctxt = ssh->authctxt;
252	int r, authenticated;
253	const char *displayname;
254
255	if (authctxt == NULL)
256		fatal("No authentication or GSSAPI context");
257
258	/*
259	 * We don't need to check the status, because we're only enabled in
260	 * the dispatcher once the exchange is complete
261	 */
262
263	if ((r = sshpkt_get_end(ssh)) != 0)
264		fatal_fr(r, "parse packet");
265
266	authenticated = mm_ssh_gssapi_userok(authctxt->user);
267
268	authctxt->postponed = 0;
269	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
270	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
271	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
272	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
273	userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL);
274	return 0;
275}
276
277static int
278input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
279{
280	Authctxt *authctxt = ssh->authctxt;
281	Gssctxt *gssctxt;
282	int r, authenticated = 0;
283	struct sshbuf *b;
284	gss_buffer_desc mic, gssbuf;
285	const char *displayname;
286	u_char *p;
287	size_t len;
288
289	if (authctxt == NULL)
290		fatal("No authentication or GSSAPI context");
291
292	gssctxt = authctxt->methoddata;
293
294	if ((r = sshpkt_get_string(ssh, &p, &len)) != 0)
295		fatal_fr(r, "parse packet");
296	if ((b = sshbuf_new()) == NULL)
297		fatal_f("sshbuf_new failed");
298	mic.value = p;
299	mic.length = len;
300	ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
301	    "gssapi-with-mic", ssh->kex->session_id);
302
303	if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
304		fatal_f("sshbuf_mutable_ptr failed");
305	gssbuf.length = sshbuf_len(b);
306
307	if (!GSS_ERROR(mm_ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))
308		authenticated = mm_ssh_gssapi_userok(authctxt->user);
309	else
310		logit("GSSAPI MIC check failed");
311
312	sshbuf_free(b);
313	free(mic.value);
314
315	authctxt->postponed = 0;
316	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
317	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
318	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
319	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
320	userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL);
321	return 0;
322}
323
324Authmethod method_gssapi = {
325	&methodcfg_gssapi,
326	userauth_gssapi,
327};
328#endif
329