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