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