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