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