1 /* $OpenBSD: auth2-gss.c,v 1.36 2024/05/17 04:42:13 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 #include "includes.h" 28 29 #ifdef GSSAPI 30 31 #include <sys/types.h> 32 33 #include <stdarg.h> 34 35 #include "xmalloc.h" 36 #include "sshkey.h" 37 #include "hostfile.h" 38 #include "auth.h" 39 #include "ssh2.h" 40 #include "log.h" 41 #include "dispatch.h" 42 #include "sshbuf.h" 43 #include "ssherr.h" 44 #include "misc.h" 45 #include "servconf.h" 46 #include "packet.h" 47 #include "kex.h" 48 #include "ssh-gss.h" 49 #include "monitor_wrap.h" 50 51 #define SSH_GSSAPI_MAX_MECHS 2048 52 53 extern ServerOptions options; 54 extern struct authmethod_cfg methodcfg_gssapi; 55 56 static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh); 57 static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh); 58 static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); 59 static 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 */ 65 static int 66 userauth_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(mm_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 145 static int 146 input_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) 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 = mm_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 209 static int 210 input_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) 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 = mm_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 253 static int 254 input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh) 255 { 256 Authctxt *authctxt = ssh->authctxt; 257 int r, authenticated; 258 259 if (authctxt == NULL) 260 fatal("No authentication or GSSAPI context"); 261 262 /* 263 * We don't need to check the status, because we're only enabled in 264 * the dispatcher once the exchange is complete 265 */ 266 267 if ((r = sshpkt_get_end(ssh)) != 0) 268 fatal_fr(r, "parse packet"); 269 270 authenticated = mm_ssh_gssapi_userok(authctxt->user); 271 272 authctxt->postponed = 0; 273 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 274 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 275 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 276 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 277 userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL); 278 return 0; 279 } 280 281 static int 282 input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) 283 { 284 Authctxt *authctxt = ssh->authctxt; 285 Gssctxt *gssctxt; 286 int r, authenticated = 0; 287 struct sshbuf *b; 288 gss_buffer_desc mic, gssbuf; 289 u_char *p; 290 size_t len; 291 292 if (authctxt == NULL) 293 fatal("No authentication or GSSAPI context"); 294 295 gssctxt = authctxt->methoddata; 296 297 if ((r = sshpkt_get_string(ssh, &p, &len)) != 0) 298 fatal_fr(r, "parse packet"); 299 if ((b = sshbuf_new()) == NULL) 300 fatal_f("sshbuf_new failed"); 301 mic.value = p; 302 mic.length = len; 303 ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, 304 "gssapi-with-mic", ssh->kex->session_id); 305 306 if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) 307 fatal_f("sshbuf_mutable_ptr failed"); 308 gssbuf.length = sshbuf_len(b); 309 310 if (!GSS_ERROR(mm_ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))) 311 authenticated = mm_ssh_gssapi_userok(authctxt->user); 312 else 313 logit("GSSAPI MIC check failed"); 314 315 sshbuf_free(b); 316 free(mic.value); 317 318 authctxt->postponed = 0; 319 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 320 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); 321 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); 322 ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 323 userauth_finish(ssh, authenticated, "gssapi-with-mic", NULL); 324 return 0; 325 } 326 327 Authmethod method_gssapi = { 328 &methodcfg_gssapi, 329 userauth_gssapi, 330 }; 331 332 #endif /* GSSAPI */ 333