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