1 /* $OpenBSD: gss-genr.c,v 1.20 2009/06/22 05:39:28 dtucker 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 #ifdef GSSAPI 28 29 #include <sys/param.h> 30 31 #include <stdarg.h> 32 #include <string.h> 33 34 #include "xmalloc.h" 35 #include "buffer.h" 36 #include "log.h" 37 #include "ssh2.h" 38 39 #include "ssh-gss.h" 40 41 extern u_char *session_id2; 42 extern u_int session_id2_len; 43 44 /* Check that the OID in a data stream matches that in the context */ 45 int 46 ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) 47 { 48 return (ctx != NULL && ctx->oid != GSS_C_NO_OID && 49 ctx->oid->length == len && 50 memcmp(ctx->oid->elements, data, len) == 0); 51 } 52 53 /* Set the contexts OID from a data stream */ 54 void 55 ssh_gssapi_set_oid_data(Gssctxt *ctx, void *data, size_t len) 56 { 57 if (ctx->oid != GSS_C_NO_OID) { 58 xfree(ctx->oid->elements); 59 xfree(ctx->oid); 60 } 61 ctx->oid = xmalloc(sizeof(gss_OID_desc)); 62 ctx->oid->length = len; 63 ctx->oid->elements = xmalloc(len); 64 memcpy(ctx->oid->elements, data, len); 65 } 66 67 /* Set the contexts OID */ 68 void 69 ssh_gssapi_set_oid(Gssctxt *ctx, gss_OID oid) 70 { 71 ssh_gssapi_set_oid_data(ctx, oid->elements, oid->length); 72 } 73 74 /* All this effort to report an error ... */ 75 void 76 ssh_gssapi_error(Gssctxt *ctxt) 77 { 78 char *s; 79 80 s = ssh_gssapi_last_error(ctxt, NULL, NULL); 81 debug("%s", s); 82 xfree(s); 83 } 84 85 char * 86 ssh_gssapi_last_error(Gssctxt *ctxt, OM_uint32 *major_status, 87 OM_uint32 *minor_status) 88 { 89 OM_uint32 lmin; 90 gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; 91 OM_uint32 ctx; 92 Buffer b; 93 char *ret; 94 95 buffer_init(&b); 96 97 if (major_status != NULL) 98 *major_status = ctxt->major; 99 if (minor_status != NULL) 100 *minor_status = ctxt->minor; 101 102 ctx = 0; 103 /* The GSSAPI error */ 104 do { 105 gss_display_status(&lmin, ctxt->major, 106 GSS_C_GSS_CODE, ctxt->oid, &ctx, &msg); 107 108 buffer_append(&b, msg.value, msg.length); 109 buffer_put_char(&b, '\n'); 110 111 gss_release_buffer(&lmin, &msg); 112 } while (ctx != 0); 113 114 /* The mechanism specific error */ 115 do { 116 gss_display_status(&lmin, ctxt->minor, 117 GSS_C_MECH_CODE, ctxt->oid, &ctx, &msg); 118 119 buffer_append(&b, msg.value, msg.length); 120 buffer_put_char(&b, '\n'); 121 122 gss_release_buffer(&lmin, &msg); 123 } while (ctx != 0); 124 125 buffer_put_char(&b, '\0'); 126 ret = xmalloc(buffer_len(&b)); 127 buffer_get(&b, ret, buffer_len(&b)); 128 buffer_free(&b); 129 return (ret); 130 } 131 132 /* 133 * Initialise our GSSAPI context. We use this opaque structure to contain all 134 * of the data which both the client and server need to persist across 135 * {accept,init}_sec_context calls, so that when we do it from the userauth 136 * stuff life is a little easier 137 */ 138 void 139 ssh_gssapi_build_ctx(Gssctxt **ctx) 140 { 141 *ctx = xcalloc(1, sizeof (Gssctxt)); 142 (*ctx)->context = GSS_C_NO_CONTEXT; 143 (*ctx)->name = GSS_C_NO_NAME; 144 (*ctx)->oid = GSS_C_NO_OID; 145 (*ctx)->creds = GSS_C_NO_CREDENTIAL; 146 (*ctx)->client = GSS_C_NO_NAME; 147 (*ctx)->client_creds = GSS_C_NO_CREDENTIAL; 148 } 149 150 /* Delete our context, providing it has been built correctly */ 151 void 152 ssh_gssapi_delete_ctx(Gssctxt **ctx) 153 { 154 OM_uint32 ms; 155 156 if ((*ctx) == NULL) 157 return; 158 if ((*ctx)->context != GSS_C_NO_CONTEXT) 159 gss_delete_sec_context(&ms, &(*ctx)->context, GSS_C_NO_BUFFER); 160 if ((*ctx)->name != GSS_C_NO_NAME) 161 gss_release_name(&ms, &(*ctx)->name); 162 if ((*ctx)->oid != GSS_C_NO_OID) { 163 xfree((*ctx)->oid->elements); 164 xfree((*ctx)->oid); 165 (*ctx)->oid = GSS_C_NO_OID; 166 } 167 if ((*ctx)->creds != GSS_C_NO_CREDENTIAL) 168 gss_release_cred(&ms, &(*ctx)->creds); 169 if ((*ctx)->client != GSS_C_NO_NAME) 170 gss_release_name(&ms, &(*ctx)->client); 171 if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL) 172 gss_release_cred(&ms, &(*ctx)->client_creds); 173 174 xfree(*ctx); 175 *ctx = NULL; 176 } 177 178 /* 179 * Wrapper to init_sec_context 180 * Requires that the context contains: 181 * oid 182 * server name (from ssh_gssapi_import_name) 183 */ 184 OM_uint32 185 ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, 186 gss_buffer_desc* send_tok, OM_uint32 *flags) 187 { 188 int deleg_flag = 0; 189 190 if (deleg_creds) { 191 deleg_flag = GSS_C_DELEG_FLAG; 192 debug("Delegating credentials"); 193 } 194 195 ctx->major = gss_init_sec_context(&ctx->minor, 196 GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, 197 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 198 0, NULL, recv_tok, NULL, send_tok, flags, NULL); 199 200 if (GSS_ERROR(ctx->major)) 201 ssh_gssapi_error(ctx); 202 203 return (ctx->major); 204 } 205 206 /* Create a service name for the given host */ 207 OM_uint32 208 ssh_gssapi_import_name(Gssctxt *ctx, const char *host) 209 { 210 gss_buffer_desc gssbuf; 211 char *val; 212 213 xasprintf(&val, "host@%s", host); 214 gssbuf.value = val; 215 gssbuf.length = strlen(gssbuf.value); 216 217 if ((ctx->major = gss_import_name(&ctx->minor, 218 &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name))) 219 ssh_gssapi_error(ctx); 220 221 xfree(gssbuf.value); 222 return (ctx->major); 223 } 224 225 OM_uint32 226 ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) 227 { 228 if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, 229 GSS_C_QOP_DEFAULT, buffer, hash))) 230 ssh_gssapi_error(ctx); 231 232 return (ctx->major); 233 } 234 235 void 236 ssh_gssapi_buildmic(Buffer *b, const char *user, const char *service, 237 const char *context) 238 { 239 buffer_init(b); 240 buffer_put_string(b, session_id2, session_id2_len); 241 buffer_put_char(b, SSH2_MSG_USERAUTH_REQUEST); 242 buffer_put_cstring(b, user); 243 buffer_put_cstring(b, service); 244 buffer_put_cstring(b, context); 245 } 246 247 int 248 ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) 249 { 250 gss_buffer_desc token = GSS_C_EMPTY_BUFFER; 251 OM_uint32 major, minor; 252 gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; 253 254 /* RFC 4462 says we MUST NOT do SPNEGO */ 255 if (oid->length == spnego_oid.length && 256 (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) 257 return 0; /* false */ 258 259 ssh_gssapi_build_ctx(ctx); 260 ssh_gssapi_set_oid(*ctx, oid); 261 major = ssh_gssapi_import_name(*ctx, host); 262 if (!GSS_ERROR(major)) { 263 major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, 264 NULL); 265 gss_release_buffer(&minor, &token); 266 if ((*ctx)->context != GSS_C_NO_CONTEXT) 267 gss_delete_sec_context(&minor, &(*ctx)->context, 268 GSS_C_NO_BUFFER); 269 } 270 271 if (GSS_ERROR(major)) 272 ssh_gssapi_delete_ctx(ctx); 273 274 return (!GSS_ERROR(major)); 275 } 276 277 #endif /* GSSAPI */ 278