Annotation of src/usr.bin/ssh/gss-serv.c, Revision 1.22
1.22 ! djm 1: /* $OpenBSD: gss-serv.c,v 1.21 2007/06/12 08:20:00 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.1 markus 228: if (ename->length < offset+name->length)
1.3 markus 229: return GSS_S_FAILURE;
230:
1.2 markus 231: name->value = xmalloc(name->length+1);
1.14 stevesk 232: memcpy(name->value, tok+offset, name->length);
1.2 markus 233: ((char *)name->value)[name->length] = 0;
234:
1.1 markus 235: return GSS_S_COMPLETE;
1.3 markus 236: }
1.1 markus 237:
238: /* Extract the client details from a given context. This can only reliably
239: * be called once for a context */
240:
1.11 stevesk 241: /* Privileged (called from accept_secure_ctx) */
1.1 markus 242: OM_uint32
243: ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
244: {
245: int i = 0;
246:
247: gss_buffer_desc ename;
1.3 markus 248:
1.1 markus 249: client->mech = NULL;
250:
251: while (supported_mechs[i]->name != NULL) {
252: if (supported_mechs[i]->oid.length == ctx->oid->length &&
253: (memcmp(supported_mechs[i]->oid.elements,
254: ctx->oid->elements, ctx->oid->length) == 0))
255: client->mech = supported_mechs[i];
256: i++;
257: }
258:
1.3 markus 259: if (client->mech == NULL)
1.1 markus 260: return GSS_S_FAILURE;
1.3 markus 261:
262: if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
1.1 markus 263: &client->displayname, NULL))) {
1.3 markus 264: ssh_gssapi_error(ctx);
265: return (ctx->major);
1.1 markus 266: }
1.3 markus 267:
268: if ((ctx->major = gss_export_name(&ctx->minor, ctx->client,
1.1 markus 269: &ename))) {
270: ssh_gssapi_error(ctx);
271: return (ctx->major);
272: }
1.3 markus 273:
1.1 markus 274: if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
275: &client->exportedname))) {
276: return (ctx->major);
277: }
278:
279: /* We can't copy this structure, so we just move the pointer to it */
280: client->creds = ctx->client_creds;
281: ctx->client_creds = GSS_C_NO_CREDENTIAL;
282: return (ctx->major);
283: }
284:
1.4 markus 285: /* As user - called on fatal/exit */
1.1 markus 286: void
1.4 markus 287: ssh_gssapi_cleanup_creds(void)
1.1 markus 288: {
289: if (gssapi_client.store.filename != NULL) {
290: /* Unlink probably isn't sufficient */
1.14 stevesk 291: debug("removing gssapi cred file\"%s\"",
292: gssapi_client.store.filename);
1.1 markus 293: unlink(gssapi_client.store.filename);
294: }
295: }
296:
297: /* As user */
298: void
299: ssh_gssapi_storecreds(void)
300: {
301: if (gssapi_client.mech && gssapi_client.mech->storecreds) {
302: (*gssapi_client.mech->storecreds)(&gssapi_client);
303: } else
304: debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
305: }
306:
307: /* This allows GSSAPI methods to do things to the childs environment based
308: * on the passed authentication process and credentials.
309: */
310: /* As user */
311: void
312: ssh_gssapi_do_child(char ***envp, u_int *envsizep)
313: {
314:
315: if (gssapi_client.store.envvar != NULL &&
316: gssapi_client.store.envval != NULL) {
317: debug("Setting %s to %s", gssapi_client.store.envvar,
1.13 stevesk 318: gssapi_client.store.envval);
1.1 markus 319: child_set_env(envp, envsizep, gssapi_client.store.envvar,
1.7 djm 320: gssapi_client.store.envval);
1.1 markus 321: }
322: }
323:
1.9 djm 324: /* Privileged */
1.1 markus 325: int
326: ssh_gssapi_userok(char *user)
327: {
1.8 djm 328: OM_uint32 lmin;
329:
1.1 markus 330: if (gssapi_client.exportedname.length == 0 ||
331: gssapi_client.exportedname.value == NULL) {
332: debug("No suitable client data");
333: return 0;
334: }
335: if (gssapi_client.mech && gssapi_client.mech->userok)
1.8 djm 336: if ((*gssapi_client.mech->userok)(&gssapi_client, user))
337: return 1;
338: else {
339: /* Destroy delegated credentials if userok fails */
340: gss_release_buffer(&lmin, &gssapi_client.displayname);
341: gss_release_buffer(&lmin, &gssapi_client.exportedname);
342: gss_release_cred(&lmin, &gssapi_client.creds);
343: memset(&gssapi_client, 0, sizeof(ssh_gssapi_client));
344: return 0;
345: }
1.1 markus 346: else
347: debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
348: return (0);
1.5 markus 349: }
350:
1.11 stevesk 351: /* Privileged */
1.5 markus 352: OM_uint32
353: ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
354: {
355: ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
356: gssbuf, gssmic, NULL);
357:
358: return (ctx->major);
1.1 markus 359: }
360:
361: #endif