1 /* $OpenBSD: gss-genr.c,v 1.14 2006/08/18 13:54:54 djm Exp $ */
4 * Copyright (c) 2001-2006 Simon Wilkinson. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
31 #include <sys/types.h>
32 #include <sys/param.h>
45 extern u_char
*session_id2
;
46 extern u_int session_id2_len
;
48 /* Check that the OID in a data stream matches that in the context */
50 ssh_gssapi_check_oid(Gssctxt
*ctx
, void *data
, size_t len
)
52 return (ctx
!= NULL
&& ctx
->oid
!= GSS_C_NO_OID
&&
53 ctx
->oid
->length
== len
&&
54 memcmp(ctx
->oid
->elements
, data
, len
) == 0);
57 /* Set the contexts OID from a data stream */
59 ssh_gssapi_set_oid_data(Gssctxt
*ctx
, void *data
, size_t len
)
61 if (ctx
->oid
!= GSS_C_NO_OID
) {
62 xfree(ctx
->oid
->elements
);
65 ctx
->oid
= xmalloc(sizeof(gss_OID_desc
));
66 ctx
->oid
->length
= len
;
67 ctx
->oid
->elements
= xmalloc(len
);
68 memcpy(ctx
->oid
->elements
, data
, len
);
71 /* Set the contexts OID */
73 ssh_gssapi_set_oid(Gssctxt
*ctx
, gss_OID oid
)
75 ssh_gssapi_set_oid_data(ctx
, oid
->elements
, oid
->length
);
78 /* All this effort to report an error ... */
80 ssh_gssapi_error(Gssctxt
*ctxt
)
84 s
= ssh_gssapi_last_error(ctxt
, NULL
, NULL
);
90 ssh_gssapi_last_error(Gssctxt
*ctxt
, OM_uint32
*major_status
,
91 OM_uint32
*minor_status
)
94 gss_buffer_desc msg
= GSS_C_EMPTY_BUFFER
;
101 if (major_status
!= NULL
)
102 *major_status
= ctxt
->major
;
103 if (minor_status
!= NULL
)
104 *minor_status
= ctxt
->minor
;
107 /* The GSSAPI error */
109 gss_display_status(&lmin
, ctxt
->major
,
110 GSS_C_GSS_CODE
, GSS_C_NULL_OID
, &ctx
, &msg
);
112 buffer_append(&b
, msg
.value
, msg
.length
);
113 buffer_put_char(&b
, '\n');
115 gss_release_buffer(&lmin
, &msg
);
118 /* The mechanism specific error */
120 gss_display_status(&lmin
, ctxt
->minor
,
121 GSS_C_MECH_CODE
, GSS_C_NULL_OID
, &ctx
, &msg
);
123 buffer_append(&b
, msg
.value
, msg
.length
);
124 buffer_put_char(&b
, '\n');
126 gss_release_buffer(&lmin
, &msg
);
129 buffer_put_char(&b
, '\0');
130 ret
= xmalloc(buffer_len(&b
));
131 buffer_get(&b
, ret
, buffer_len(&b
));
137 * Initialise our GSSAPI context. We use this opaque structure to contain all
138 * of the data which both the client and server need to persist across
139 * {accept,init}_sec_context calls, so that when we do it from the userauth
140 * stuff life is a little easier
143 ssh_gssapi_build_ctx(Gssctxt
**ctx
)
145 *ctx
= xcalloc(1, sizeof (Gssctxt
));
146 (*ctx
)->context
= GSS_C_NO_CONTEXT
;
147 (*ctx
)->name
= GSS_C_NO_NAME
;
148 (*ctx
)->oid
= GSS_C_NO_OID
;
149 (*ctx
)->creds
= GSS_C_NO_CREDENTIAL
;
150 (*ctx
)->client
= GSS_C_NO_NAME
;
151 (*ctx
)->client_creds
= GSS_C_NO_CREDENTIAL
;
154 /* Delete our context, providing it has been built correctly */
156 ssh_gssapi_delete_ctx(Gssctxt
**ctx
)
162 if ((*ctx
)->context
!= GSS_C_NO_CONTEXT
)
163 gss_delete_sec_context(&ms
, &(*ctx
)->context
, GSS_C_NO_BUFFER
);
164 if ((*ctx
)->name
!= GSS_C_NO_NAME
)
165 gss_release_name(&ms
, &(*ctx
)->name
);
166 if ((*ctx
)->oid
!= GSS_C_NO_OID
) {
167 xfree((*ctx
)->oid
->elements
);
169 (*ctx
)->oid
= GSS_C_NO_OID
;
171 if ((*ctx
)->creds
!= GSS_C_NO_CREDENTIAL
)
172 gss_release_cred(&ms
, &(*ctx
)->creds
);
173 if ((*ctx
)->client
!= GSS_C_NO_NAME
)
174 gss_release_name(&ms
, &(*ctx
)->client
);
175 if ((*ctx
)->client_creds
!= GSS_C_NO_CREDENTIAL
)
176 gss_release_cred(&ms
, &(*ctx
)->client_creds
);
183 * Wrapper to init_sec_context
184 * Requires that the context contains:
186 * server name (from ssh_gssapi_import_name)
189 ssh_gssapi_init_ctx(Gssctxt
*ctx
, int deleg_creds
, gss_buffer_desc
*recv_tok
,
190 gss_buffer_desc
* send_tok
, OM_uint32
*flags
)
195 deleg_flag
= GSS_C_DELEG_FLAG
;
196 debug("Delegating credentials");
199 ctx
->major
= gss_init_sec_context(&ctx
->minor
,
200 GSS_C_NO_CREDENTIAL
, &ctx
->context
, ctx
->name
, ctx
->oid
,
201 GSS_C_MUTUAL_FLAG
| GSS_C_INTEG_FLAG
| deleg_flag
,
202 0, NULL
, recv_tok
, NULL
, send_tok
, flags
, NULL
);
204 if (GSS_ERROR(ctx
->major
))
205 ssh_gssapi_error(ctx
);
210 /* Create a service name for the given host */
212 ssh_gssapi_import_name(Gssctxt
*ctx
, const char *host
)
214 gss_buffer_desc gssbuf
;
217 xasprintf(&val
, "host@%s", host
);
219 gssbuf
.length
= strlen(gssbuf
.value
);
221 if ((ctx
->major
= gss_import_name(&ctx
->minor
,
222 &gssbuf
, GSS_C_NT_HOSTBASED_SERVICE
, &ctx
->name
)))
223 ssh_gssapi_error(ctx
);
229 /* Acquire credentials for a server running on the current host.
230 * Requires that the context structure contains a valid OID
233 /* Returns a GSSAPI error code */
235 ssh_gssapi_acquire_cred(Gssctxt
*ctx
)
238 char lname
[MAXHOSTNAMELEN
];
241 gss_create_empty_oid_set(&status
, &oidset
);
242 gss_add_oid_set_member(&status
, ctx
->oid
, &oidset
);
244 if (gethostname(lname
, MAXHOSTNAMELEN
)) {
245 gss_release_oid_set(&status
, &oidset
);
249 if (GSS_ERROR(ssh_gssapi_import_name(ctx
, lname
))) {
250 gss_release_oid_set(&status
, &oidset
);
254 if ((ctx
->major
= gss_acquire_cred(&ctx
->minor
,
255 ctx
->name
, 0, oidset
, GSS_C_ACCEPT
, &ctx
->creds
, NULL
, NULL
)))
256 ssh_gssapi_error(ctx
);
258 gss_release_oid_set(&status
, &oidset
);
263 ssh_gssapi_sign(Gssctxt
*ctx
, gss_buffer_t buffer
, gss_buffer_t hash
)
265 if ((ctx
->major
= gss_get_mic(&ctx
->minor
, ctx
->context
,
266 GSS_C_QOP_DEFAULT
, buffer
, hash
)))
267 ssh_gssapi_error(ctx
);
273 ssh_gssapi_buildmic(Buffer
*b
, const char *user
, const char *service
,
277 buffer_put_string(b
, session_id2
, session_id2_len
);
278 buffer_put_char(b
, SSH2_MSG_USERAUTH_REQUEST
);
279 buffer_put_cstring(b
, user
);
280 buffer_put_cstring(b
, service
);
281 buffer_put_cstring(b
, context
);
285 ssh_gssapi_server_ctx(Gssctxt
**ctx
, gss_OID oid
)
288 ssh_gssapi_delete_ctx(ctx
);
289 ssh_gssapi_build_ctx(ctx
);
290 ssh_gssapi_set_oid(*ctx
, oid
);
291 return (ssh_gssapi_acquire_cred(*ctx
));
295 ssh_gssapi_check_mechanism(Gssctxt
**ctx
, gss_OID oid
, char *host
)
297 gss_buffer_desc token
= GSS_C_EMPTY_BUFFER
;
298 OM_uint32 major
, minor
;
299 gss_OID_desc spnego_oid
= {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
301 /* RFC 4462 says we MUST NOT do SPNEGO */
302 if (oid
->length
== spnego_oid
.length
&&
303 (memcmp(oid
->elements
, spnego_oid
.elements
, oid
->length
) == 0))
306 ssh_gssapi_build_ctx(ctx
);
307 ssh_gssapi_set_oid(*ctx
, oid
);
308 major
= ssh_gssapi_import_name(*ctx
, host
);
309 if (!GSS_ERROR(major
)) {
310 major
= ssh_gssapi_init_ctx(*ctx
, 0, GSS_C_NO_BUFFER
, &token
,
312 gss_release_buffer(&minor
, &token
);
313 gss_delete_sec_context(&minor
, &(*ctx
)->context
,
317 if (GSS_ERROR(major
))
318 ssh_gssapi_delete_ctx(ctx
);
320 return (!GSS_ERROR(major
));