1/*
2 * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * 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 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "test_locl.h"
35#include <gssapi/gssapi.h>
36#include <gssapi/gssapi_krb5.h>
37#include <gssapi/gssapi_spnego.h>
38#include "gss_common.h"
39RCSID("$Id$");
40
41static int
42process_it(int sock,
43	   gss_ctx_id_t context_hdl,
44	   gss_name_t client_name
45	   )
46{
47    OM_uint32 maj_stat, min_stat;
48    gss_buffer_desc real_input_token, real_output_token;
49    gss_buffer_t input_token = &real_input_token,
50	output_token = &real_output_token;
51    gss_name_t server_name;
52    int conf_flag;
53
54    print_gss_name("User is", client_name);
55
56    maj_stat = gss_inquire_context(&min_stat,
57				   context_hdl,
58				   NULL,
59				   &server_name,
60				   NULL,
61				   NULL,
62				   NULL,
63				   NULL,
64				   NULL);
65    if (GSS_ERROR(maj_stat))
66	gss_err (1, min_stat, "gss_inquire_context");
67
68    print_gss_name("Server is", server_name);
69
70    maj_stat = gss_release_name(&min_stat, &server_name);
71    if (GSS_ERROR(maj_stat))
72	gss_err (1, min_stat, "gss_release_name");
73
74    /* gss_verify_mic */
75
76    read_token (sock, input_token);
77    read_token (sock, output_token);
78
79    maj_stat = gss_verify_mic (&min_stat,
80			       context_hdl,
81			       input_token,
82			       output_token,
83			       NULL);
84    if (GSS_ERROR(maj_stat))
85	gss_err (1, min_stat, "gss_verify_mic");
86
87    fprintf (stderr, "gss_verify_mic: %.*s\n", (int)input_token->length,
88	    (char *)input_token->value);
89
90    gss_release_buffer (&min_stat, input_token);
91    gss_release_buffer (&min_stat, output_token);
92
93    /* gss_unwrap */
94
95    read_token (sock, input_token);
96
97    maj_stat = gss_unwrap (&min_stat,
98			   context_hdl,
99			   input_token,
100			   output_token,
101			   &conf_flag,
102			   NULL);
103    if(GSS_ERROR(maj_stat))
104	gss_err (1, min_stat, "gss_unwrap");
105
106    fprintf (stderr, "gss_unwrap: %.*s %s\n", (int)output_token->length,
107	    (char *)output_token->value,
108	     conf_flag ? "CONF" : "INT");
109
110    gss_release_buffer (&min_stat, input_token);
111    gss_release_buffer (&min_stat, output_token);
112
113    read_token (sock, input_token);
114
115    maj_stat = gss_unwrap (&min_stat,
116			   context_hdl,
117			   input_token,
118			   output_token,
119			   &conf_flag,
120			   NULL);
121    if(GSS_ERROR(maj_stat))
122	gss_err (1, min_stat, "gss_unwrap");
123
124    fprintf (stderr, "gss_unwrap: %.*s %s\n", (int)output_token->length,
125	     (char *)output_token->value,
126	     conf_flag ? "CONF" : "INT");
127
128    gss_release_buffer (&min_stat, input_token);
129    gss_release_buffer (&min_stat, output_token);
130
131    return 0;
132}
133
134static int
135proto (int sock, const char *service)
136{
137    struct sockaddr_in remote, local;
138    socklen_t addrlen;
139    gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT;
140    gss_buffer_desc real_input_token, real_output_token;
141    gss_buffer_t input_token = &real_input_token,
142	output_token = &real_output_token;
143    OM_uint32 maj_stat, min_stat;
144    gss_name_t client_name;
145    struct gss_channel_bindings_struct input_chan_bindings;
146    gss_cred_id_t delegated_cred_handle = NULL;
147    krb5_ccache ccache;
148    u_char init_buf[4];
149    u_char acct_buf[4];
150    gss_OID mech_oid;
151    char *mech, *p;
152
153    addrlen = sizeof(local);
154    if (getsockname (sock, (struct sockaddr *)&local, &addrlen) < 0
155	|| addrlen != sizeof(local))
156	err (1, "getsockname)");
157
158    addrlen = sizeof(remote);
159    if (getpeername (sock, (struct sockaddr *)&remote, &addrlen) < 0
160	|| addrlen != sizeof(remote))
161	err (1, "getpeername");
162
163    input_chan_bindings.initiator_addrtype = GSS_C_AF_INET;
164    input_chan_bindings.initiator_address.length = 4;
165    init_buf[0] = (remote.sin_addr.s_addr >> 24) & 0xFF;
166    init_buf[1] = (remote.sin_addr.s_addr >> 16) & 0xFF;
167    init_buf[2] = (remote.sin_addr.s_addr >>  8) & 0xFF;
168    init_buf[3] = (remote.sin_addr.s_addr >>  0) & 0xFF;
169
170    input_chan_bindings.initiator_address.value = init_buf;
171    input_chan_bindings.acceptor_addrtype = GSS_C_AF_INET;
172
173    input_chan_bindings.acceptor_address.length = 4;
174    acct_buf[0] = (local.sin_addr.s_addr >> 24) & 0xFF;
175    acct_buf[1] = (local.sin_addr.s_addr >> 16) & 0xFF;
176    acct_buf[2] = (local.sin_addr.s_addr >>  8) & 0xFF;
177    acct_buf[3] = (local.sin_addr.s_addr >>  0) & 0xFF;
178    input_chan_bindings.acceptor_address.value = acct_buf;
179    input_chan_bindings.application_data.value = emalloc(4);
180#if 0
181    * (unsigned short *)input_chan_bindings.application_data.value =
182                          remote.sin_port;
183    * ((unsigned short *)input_chan_bindings.application_data.value + 1) =
184                          local.sin_port;
185    input_chan_bindings.application_data.length = 4;
186#else
187    input_chan_bindings.application_data.length = 0;
188    input_chan_bindings.application_data.value = NULL;
189#endif
190
191    delegated_cred_handle = GSS_C_NO_CREDENTIAL;
192
193    do {
194	read_token (sock, input_token);
195	maj_stat =
196	    gss_accept_sec_context (&min_stat,
197				    &context_hdl,
198				    GSS_C_NO_CREDENTIAL,
199				    input_token,
200				    &input_chan_bindings,
201				    &client_name,
202				    &mech_oid,
203				    output_token,
204				    NULL,
205				    NULL,
206				    &delegated_cred_handle);
207	if(GSS_ERROR(maj_stat))
208	    gss_err (1, min_stat, "gss_accept_sec_context");
209	if (output_token->length != 0)
210	    write_token (sock, output_token);
211	if (GSS_ERROR(maj_stat)) {
212	    if (context_hdl != GSS_C_NO_CONTEXT)
213		gss_delete_sec_context (&min_stat,
214					&context_hdl,
215					GSS_C_NO_BUFFER);
216	    break;
217	}
218    } while(maj_stat & GSS_S_CONTINUE_NEEDED);
219
220    p = (char *)mech_oid->elements;
221    if (mech_oid->length == GSS_KRB5_MECHANISM->length
222	&& memcmp(p, GSS_KRB5_MECHANISM->elements, mech_oid->length) == 0)
223	mech = "Kerberos 5";
224    else if (mech_oid->length == GSS_SPNEGO_MECHANISM->length
225	&& memcmp(p, GSS_SPNEGO_MECHANISM->elements, mech_oid->length) == 0)
226	mech = "SPNEGO"; /* XXX Silly, wont show up */
227    else
228	mech = "Unknown";
229
230    printf("Using mech: %s\n", mech);
231
232    if (delegated_cred_handle != GSS_C_NO_CREDENTIAL) {
233       krb5_context context;
234
235       printf("Delegated cred found\n");
236
237       maj_stat = krb5_init_context(&context);
238       maj_stat = krb5_cc_resolve(context, "FILE:/tmp/krb5cc_test", &ccache);
239       maj_stat = gss_krb5_copy_ccache(&min_stat,
240				       delegated_cred_handle,
241				       ccache);
242       if (maj_stat == 0) {
243	   krb5_principal p;
244	   maj_stat = krb5_cc_get_principal(context, ccache, &p);
245	   if (maj_stat == 0) {
246	       char *name;
247	       maj_stat = krb5_unparse_name(context, p, &name);
248	       if (maj_stat == 0) {
249		   printf("Delegated user is: `%s'\n", name);
250		   free(name);
251	       }
252	       krb5_free_principal(context, p);
253	   }
254       }
255       krb5_cc_close(context, ccache);
256       gss_release_cred(&min_stat, &delegated_cred_handle);
257    }
258
259    if (fork_flag) {
260	pid_t pid;
261	int pipefd[2];
262
263	if (pipe (pipefd) < 0)
264	    err (1, "pipe");
265
266	pid = fork ();
267	if (pid < 0)
268	    err (1, "fork");
269	if (pid != 0) {
270	    gss_buffer_desc buf;
271
272	    maj_stat = gss_export_sec_context (&min_stat,
273					       &context_hdl,
274					       &buf);
275	    if (GSS_ERROR(maj_stat))
276		gss_err (1, min_stat, "gss_export_sec_context");
277	    write_token (pipefd[1], &buf);
278	    exit (0);
279	} else {
280	    gss_ctx_id_t context_hdl;
281	    gss_buffer_desc buf;
282
283	    close (pipefd[1]);
284	    read_token (pipefd[0], &buf);
285	    close (pipefd[0]);
286	    maj_stat = gss_import_sec_context (&min_stat, &buf, &context_hdl);
287	    if (GSS_ERROR(maj_stat))
288		gss_err (1, min_stat, "gss_import_sec_context");
289	    gss_release_buffer (&min_stat, &buf);
290	    return process_it (sock, context_hdl, client_name);
291	}
292    } else {
293	return process_it (sock, context_hdl, client_name);
294    }
295}
296
297static int
298doit (int port, const char *service)
299{
300    int sock, sock2;
301    struct sockaddr_in my_addr;
302    int one = 1;
303    int ret;
304
305    sock = socket (AF_INET, SOCK_STREAM, 0);
306    if (sock < 0)
307	err (1, "socket");
308
309    memset (&my_addr, 0, sizeof(my_addr));
310    my_addr.sin_family      = AF_INET;
311    my_addr.sin_port        = port;
312    my_addr.sin_addr.s_addr = INADDR_ANY;
313
314    if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
315		    (void *)&one, sizeof(one)) < 0)
316	warn ("setsockopt SO_REUSEADDR");
317
318    if (bind (sock, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0)
319	err (1, "bind");
320
321    while (1) {
322        if (listen (sock, 1) < 0)
323	    err (1, "listen");
324
325        sock2 = accept (sock, NULL, NULL);
326        if (sock2 < 0)
327	    err (1, "accept");
328
329        ret = proto (sock2, service);
330    }
331    return ret;
332}
333
334int
335main(int argc, char **argv)
336{
337    krb5_context context = NULL; /* XXX */
338    int port = server_setup(&context, argc, argv);
339    return doit (port, service);
340}
341
342