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