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