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