1 /* $OpenBSD: gss-serv.c,v 1.20 2006/08/03 03:34:42 deraadt 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 <sys/types.h> 28 29 #ifdef GSSAPI 30 31 #include <string.h> 32 33 #include "xmalloc.h" 34 #include "buffer.h" 35 #include "key.h" 36 #include "hostfile.h" 37 #include "auth.h" 38 #include "log.h" 39 #include "channels.h" 40 #include "session.h" 41 #include "misc.h" 42 43 #include "ssh-gss.h" 44 45 static ssh_gssapi_client gssapi_client = 46 { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 47 GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL}}; 48 49 ssh_gssapi_mech gssapi_null_mech = 50 { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; 51 52 #ifdef KRB5 53 extern ssh_gssapi_mech gssapi_kerberos_mech; 54 #endif 55 56 ssh_gssapi_mech* supported_mechs[]= { 57 #ifdef KRB5 58 &gssapi_kerberos_mech, 59 #endif 60 &gssapi_null_mech, 61 }; 62 63 /* Unprivileged */ 64 void 65 ssh_gssapi_supported_oids(gss_OID_set *oidset) 66 { 67 int i = 0; 68 OM_uint32 min_status; 69 int present; 70 gss_OID_set supported; 71 72 gss_create_empty_oid_set(&min_status, oidset); 73 gss_indicate_mechs(&min_status, &supported); 74 75 while (supported_mechs[i]->name != NULL) { 76 if (GSS_ERROR(gss_test_oid_set_member(&min_status, 77 &supported_mechs[i]->oid, supported, &present))) 78 present = 0; 79 if (present) 80 gss_add_oid_set_member(&min_status, 81 &supported_mechs[i]->oid, oidset); 82 i++; 83 } 84 85 gss_release_oid_set(&min_status, &supported); 86 } 87 88 89 /* Wrapper around accept_sec_context 90 * Requires that the context contains: 91 * oid 92 * credentials (from ssh_gssapi_acquire_cred) 93 */ 94 /* Privileged */ 95 OM_uint32 96 ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok, 97 gss_buffer_desc *send_tok, OM_uint32 *flags) 98 { 99 OM_uint32 status; 100 gss_OID mech; 101 102 ctx->major = gss_accept_sec_context(&ctx->minor, 103 &ctx->context, ctx->creds, recv_tok, 104 GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech, 105 send_tok, flags, NULL, &ctx->client_creds); 106 107 if (GSS_ERROR(ctx->major)) 108 ssh_gssapi_error(ctx); 109 110 if (ctx->client_creds) 111 debug("Received some client credentials"); 112 else 113 debug("Got no client credentials"); 114 115 status = ctx->major; 116 117 /* Now, if we're complete and we have the right flags, then 118 * we flag the user as also having been authenticated 119 */ 120 121 if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) && 122 (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) { 123 if (ssh_gssapi_getclient(ctx, &gssapi_client)) 124 fatal("Couldn't convert client name"); 125 } 126 127 return (status); 128 } 129 130 /* 131 * This parses an exported name, extracting the mechanism specific portion 132 * to use for ACL checking. It verifies that the name belongs the mechanism 133 * originally selected. 134 */ 135 static OM_uint32 136 ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name) 137 { 138 u_char *tok; 139 OM_uint32 offset; 140 OM_uint32 oidl; 141 142 tok = ename->value; 143 144 /* 145 * Check that ename is long enough for all of the fixed length 146 * header, and that the initial ID bytes are correct 147 */ 148 149 if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0) 150 return GSS_S_FAILURE; 151 152 /* 153 * Extract the OID, and check it. Here GSSAPI breaks with tradition 154 * and does use the OID type and length bytes. To confuse things 155 * there are two lengths - the first including these, and the 156 * second without. 157 */ 158 159 oidl = get_u16(tok+2); /* length including next two bytes */ 160 oidl = oidl-2; /* turn it into the _real_ length of the variable OID */ 161 162 /* 163 * Check the BER encoding for correct type and length, that the 164 * string is long enough and that the OID matches that in our context 165 */ 166 if (tok[4] != 0x06 || tok[5] != oidl || 167 ename->length < oidl+6 || 168 !ssh_gssapi_check_oid(ctx, tok+6, oidl)) 169 return GSS_S_FAILURE; 170 171 offset = oidl+6; 172 173 if (ename->length < offset+4) 174 return GSS_S_FAILURE; 175 176 name->length = get_u32(tok+offset); 177 offset += 4; 178 179 if (ename->length < offset+name->length) 180 return GSS_S_FAILURE; 181 182 name->value = xmalloc(name->length+1); 183 memcpy(name->value, tok+offset, name->length); 184 ((char *)name->value)[name->length] = 0; 185 186 return GSS_S_COMPLETE; 187 } 188 189 /* Extract the client details from a given context. This can only reliably 190 * be called once for a context */ 191 192 /* Privileged (called from accept_secure_ctx) */ 193 OM_uint32 194 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) 195 { 196 int i = 0; 197 198 gss_buffer_desc ename; 199 200 client->mech = NULL; 201 202 while (supported_mechs[i]->name != NULL) { 203 if (supported_mechs[i]->oid.length == ctx->oid->length && 204 (memcmp(supported_mechs[i]->oid.elements, 205 ctx->oid->elements, ctx->oid->length) == 0)) 206 client->mech = supported_mechs[i]; 207 i++; 208 } 209 210 if (client->mech == NULL) 211 return GSS_S_FAILURE; 212 213 if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, 214 &client->displayname, NULL))) { 215 ssh_gssapi_error(ctx); 216 return (ctx->major); 217 } 218 219 if ((ctx->major = gss_export_name(&ctx->minor, ctx->client, 220 &ename))) { 221 ssh_gssapi_error(ctx); 222 return (ctx->major); 223 } 224 225 if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename, 226 &client->exportedname))) { 227 return (ctx->major); 228 } 229 230 /* We can't copy this structure, so we just move the pointer to it */ 231 client->creds = ctx->client_creds; 232 ctx->client_creds = GSS_C_NO_CREDENTIAL; 233 return (ctx->major); 234 } 235 236 /* As user - called on fatal/exit */ 237 void 238 ssh_gssapi_cleanup_creds(void) 239 { 240 if (gssapi_client.store.filename != NULL) { 241 /* Unlink probably isn't sufficient */ 242 debug("removing gssapi cred file\"%s\"", 243 gssapi_client.store.filename); 244 unlink(gssapi_client.store.filename); 245 } 246 } 247 248 /* As user */ 249 void 250 ssh_gssapi_storecreds(void) 251 { 252 if (gssapi_client.mech && gssapi_client.mech->storecreds) { 253 (*gssapi_client.mech->storecreds)(&gssapi_client); 254 } else 255 debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism"); 256 } 257 258 /* This allows GSSAPI methods to do things to the childs environment based 259 * on the passed authentication process and credentials. 260 */ 261 /* As user */ 262 void 263 ssh_gssapi_do_child(char ***envp, u_int *envsizep) 264 { 265 266 if (gssapi_client.store.envvar != NULL && 267 gssapi_client.store.envval != NULL) { 268 debug("Setting %s to %s", gssapi_client.store.envvar, 269 gssapi_client.store.envval); 270 child_set_env(envp, envsizep, gssapi_client.store.envvar, 271 gssapi_client.store.envval); 272 } 273 } 274 275 /* Privileged */ 276 int 277 ssh_gssapi_userok(char *user) 278 { 279 OM_uint32 lmin; 280 281 if (gssapi_client.exportedname.length == 0 || 282 gssapi_client.exportedname.value == NULL) { 283 debug("No suitable client data"); 284 return 0; 285 } 286 if (gssapi_client.mech && gssapi_client.mech->userok) 287 if ((*gssapi_client.mech->userok)(&gssapi_client, user)) 288 return 1; 289 else { 290 /* Destroy delegated credentials if userok fails */ 291 gss_release_buffer(&lmin, &gssapi_client.displayname); 292 gss_release_buffer(&lmin, &gssapi_client.exportedname); 293 gss_release_cred(&lmin, &gssapi_client.creds); 294 memset(&gssapi_client, 0, sizeof(ssh_gssapi_client)); 295 return 0; 296 } 297 else 298 debug("ssh_gssapi_userok: Unknown GSSAPI mechanism"); 299 return (0); 300 } 301 302 /* Privileged */ 303 OM_uint32 304 ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) 305 { 306 ctx->major = gss_verify_mic(&ctx->minor, ctx->context, 307 gssbuf, gssmic, NULL); 308 309 return (ctx->major); 310 } 311 312 #endif 313