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