1 /* $OpenBSD: auth2-gss.c,v 1.32 2021/01/27 10:15:08 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 extern ServerOptions options; 47 48 static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh); 49 static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh); 50 static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); 51 static 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 */ 57 static int 58 userauth_gssapi(struct ssh *ssh) 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 133 static int 134 input_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 197 static int 198 input_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 241 static int 242 input_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 274 static int 275 input_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 325 Authmethod method_gssapi = { 326 "gssapi-with-mic", 327 userauth_gssapi, 328 &options.gss_authentication 329 }; 330 #endif 331