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