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