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