1 /* $OpenBSD: auth2-gss.c,v 1.34 2023/03/31 04:22:27 djm Exp $ */
4 * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
31 #include <sys/types.h>
49 #include "monitor_wrap.h"
51 #define SSH_GSSAPI_MAX_MECHS 2048
53 extern ServerOptions options;
55 static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh);
56 static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
57 static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh);
58 static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
61 * We only support those mechanisms that we know about (ie ones that we know
62 * how to check local user kuserok and the like)
65 userauth_gssapi(struct ssh *ssh, const char *method)
67 Authctxt *authctxt = ssh->authctxt;
68 gss_OID_desc goid = {0, NULL};
76 if ((r = sshpkt_get_u32(ssh, &mechs)) != 0)
77 fatal_fr(r, "parse packet");
80 logit_f("mechanism negotiation is not supported");
82 } else if (mechs > SSH_GSSAPI_MAX_MECHS) {
83 logit_f("too many mechanisms requested %u > %u", mechs,
84 SSH_GSSAPI_MAX_MECHS);
94 if ((r = sshpkt_get_string(ssh, &doid, &len)) != 0)
95 fatal_fr(r, "parse oid");
97 if (len > 2 && doid[0] == SSH_GSS_OIDTYPE &&
99 goid.elements = doid + 2;
100 goid.length = len - 2;
101 ssh_gssapi_test_oid_supported(&ms, &goid, &present);
103 logit_f("badly formed OID received");
105 } while (mechs > 0 && !present);
109 authctxt->server_caused_failure = 1;
113 if (!authctxt->valid || authctxt->user == NULL) {
114 debug2_f("disabled because of invalid user");
119 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) {
121 ssh_gssapi_delete_ctx(&ctxt);
123 authctxt->server_caused_failure = 1;
127 authctxt->methoddata = (void *)ctxt;
129 /* Return the OID that we received */
130 if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE)) != 0 ||
131 (r = sshpkt_put_string(ssh, doid, len)) != 0 ||
132 (r = sshpkt_send(ssh)) != 0)
133 fatal_fr(r, "send packet");
137 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
138 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
139 authctxt->postponed = 1;
145 input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh)
147 Authctxt *authctxt = ssh->authctxt;
149 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
150 gss_buffer_desc recv_tok;
151 OM_uint32 maj_status, min_status, flags;
156 if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
157 fatal("No authentication or GSSAPI context");
159 gssctxt = authctxt->methoddata;
160 if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
161 (r = sshpkt_get_end(ssh)) != 0)
162 fatal_fr(r, "parse packet");
165 recv_tok.length = len;
166 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
171 if (GSS_ERROR(maj_status)) {
172 if (send_tok.length != 0) {
173 if ((r = sshpkt_start(ssh,
174 SSH2_MSG_USERAUTH_GSSAPI_ERRTOK)) != 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 ERRTOK packet");
180 authctxt->postponed = 0;
181 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
182 userauth_finish(ssh, 0, "gssapi-with-mic", NULL);
184 if (send_tok.length != 0) {
185 if ((r = sshpkt_start(ssh,
186 SSH2_MSG_USERAUTH_GSSAPI_TOKEN)) != 0 ||
187 (r = sshpkt_put_string(ssh, send_tok.value,
188 send_tok.length)) != 0 ||
189 (r = sshpkt_send(ssh)) != 0)
190 fatal_fr(r, "send TOKEN packet");
192 if (maj_status == GSS_S_COMPLETE) {
193 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
194 if (flags & GSS_C_INTEG_FLAG)
195 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC,
198 ssh_dispatch_set(ssh,
199 SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
200 &input_gssapi_exchange_complete);
204 gss_release_buffer(&min_status, &send_tok);
209 input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh)
211 Authctxt *authctxt = ssh->authctxt;
213 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
214 gss_buffer_desc recv_tok;
215 OM_uint32 maj_status;
220 if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
221 fatal("No authentication or GSSAPI context");
223 gssctxt = authctxt->methoddata;
224 if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
225 (r = sshpkt_get_end(ssh)) != 0)
226 fatal_fr(r, "parse packet");
228 recv_tok.length = len;
230 /* Push the error token into GSSAPI to see what it says */
231 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
234 free(recv_tok.value);
236 /* We can't return anything to the client, even if we wanted to */
237 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
238 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
240 /* The client will have already moved on to the next auth */
242 gss_release_buffer(&maj_status, &send_tok);
247 * This is called when the client thinks we've completed authentication.
248 * It should only be enabled in the dispatch handler by the function above,
249 * which only enables it once the GSSAPI exchange is complete.
253 input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
255 Authctxt *authctxt = ssh->authctxt;
256 int r, authenticated;
257 const char *displayname;
259 if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
260 fatal("No authentication or GSSAPI context");
263 * We don't need to check the status, because we're only enabled in
264 * the dispatcher once the exchange is complete
267 if ((r = sshpkt_get_end(ssh)) != 0)
268 fatal_fr(r, "parse packet");
270 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
272 if ((!use_privsep || mm_is_monitor()) &&
273 (displayname = ssh_gssapi_displayname()) != NULL)
274 auth2_record_info(authctxt, "%s", displayname);
276 authctxt->postponed = 0;
277 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
278 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
279 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
280 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
281 userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL);
286 input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
288 Authctxt *authctxt = ssh->authctxt;
290 int r, authenticated = 0;
292 gss_buffer_desc mic, gssbuf;
293 const char *displayname;
297 if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
298 fatal("No authentication or GSSAPI context");
300 gssctxt = authctxt->methoddata;
302 if ((r = sshpkt_get_string(ssh, &p, &len)) != 0)
303 fatal_fr(r, "parse packet");
304 if ((b = sshbuf_new()) == NULL)
305 fatal_f("sshbuf_new failed");
308 ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
309 "gssapi-with-mic", ssh->kex->session_id);
311 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
312 fatal_f("sshbuf_mutable_ptr failed");
313 gssbuf.length = sshbuf_len(b);
315 if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
316 authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
318 logit("GSSAPI MIC check failed");
323 if ((!use_privsep || mm_is_monitor()) &&
324 (displayname = ssh_gssapi_displayname()) != NULL)
325 auth2_record_info(authctxt, "%s", displayname);
327 authctxt->postponed = 0;
328 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
329 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
330 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
331 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
332 userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL);
336 Authmethod method_gssapi = {
340 &options.gss_authentication