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