1/*-
2 * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3 * Authors: Doug Rabson <dfr@rabson.org>
4 * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
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 AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD$
28 */
29
30#ifdef __FreeBSD__
31#include <sys/cdefs.h>
32#else
33#define __unused
34#endif
35
36#include <ctype.h>
37#include <err.h>
38#include <netdb.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44#include <rpc/rpc.h>
45#include <rpc/rpcsec_gss.h>
46
47static rpc_gss_principal_t server_acl = NULL;
48
49static void
50usage(void)
51{
52	printf("rpctest client | server\n");
53	exit(1);
54}
55
56static void
57print_principal(rpc_gss_principal_t principal)
58{
59	int i, len, n;
60	uint8_t *p;
61
62	len = principal->len;
63	p = (uint8_t *) principal->name;
64	while (len > 0) {
65		n = len;
66		if (n > 16)
67			n = 16;
68		for (i = 0; i < n; i++)
69			printf("%02x ", p[i]);
70		for (; i < 16; i++)
71			printf("   ");
72		printf("|");
73		for (i = 0; i < n; i++)
74			printf("%c", isprint(p[i]) ? p[i] : '.');
75		printf("|\n");
76		len -= n;
77		p += n;
78	}
79}
80
81static void
82test_client(int argc, const char **argv)
83{
84	rpcproc_t prog = 123456;
85	rpcvers_t vers = 1;
86	const char *netid = "tcp";
87	char hostname[128], service[128+5];
88	CLIENT *client;
89	AUTH *auth;
90	const char **mechs;
91	rpc_gss_options_req_t options_req;
92	rpc_gss_options_ret_t options_ret;
93	rpc_gss_service_t svc;
94	struct timeval tv;
95	enum clnt_stat stat;
96
97	if (argc == 2)
98		strlcpy(hostname, argv[1], sizeof(hostname));
99	else
100		gethostname(hostname, sizeof(hostname));
101
102	client = clnt_create(hostname, prog, vers, netid);
103	if (!client) {
104		printf("rpc_createerr.cf_stat = %d\n",
105		    rpc_createerr.cf_stat);
106		printf("rpc_createerr.cf_error.re_errno = %d\n",
107		    rpc_createerr.cf_error.re_errno);
108		return;
109	}
110
111	strcpy(service, "host");
112	strcat(service, "@");
113	strcat(service, hostname);
114
115	mechs = rpc_gss_get_mechanisms();
116	auth = NULL;
117	while (*mechs) {
118		options_req.req_flags = GSS_C_MUTUAL_FLAG;
119		options_req.time_req = 600;
120		options_req.my_cred = GSS_C_NO_CREDENTIAL;
121		options_req.input_channel_bindings = NULL;
122		auth = rpc_gss_seccreate(client, service,
123		    *mechs,
124		    rpc_gss_svc_none,
125		    NULL,
126		    &options_req,
127		    &options_ret);
128		if (auth)
129			break;
130		mechs++;
131	}
132	if (!auth) {
133		clnt_pcreateerror("rpc_gss_seccreate");
134		printf("Can't authenticate with server %s.\n",
135		    hostname);
136		exit(1);
137	}
138	client->cl_auth = auth;
139
140	for (svc = rpc_gss_svc_none; svc <= rpc_gss_svc_privacy; svc++) {
141		const char *svc_names[] = {
142			"rpc_gss_svc_default",
143			"rpc_gss_svc_none",
144			"rpc_gss_svc_integrity",
145			"rpc_gss_svc_privacy"
146		};
147		int num;
148
149		rpc_gss_set_defaults(auth, svc, NULL);
150		tv.tv_sec = 5;
151		tv.tv_usec = 0;
152		num = 42;
153		stat = CLNT_CALL(client, 1,
154		    (xdrproc_t) xdr_int, (char *) &num,
155		    (xdrproc_t) xdr_int, (char *) &num, tv);
156		if (stat == RPC_SUCCESS) {
157			printf("succeeded with %s\n", svc_names[svc]);
158			if (num != 142)
159				printf("unexpected reply %d\n", num);
160		} else {
161			clnt_perror(client, "call failed");
162		}
163	}
164	AUTH_DESTROY(auth);
165	CLNT_DESTROY(client);
166}
167
168static void
169server_program_1(struct svc_req *rqstp, register SVCXPRT *transp)
170{
171	rpc_gss_rawcred_t *rcred;
172	rpc_gss_ucred_t *ucred;
173	int		i, num;
174
175	if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) {
176		svcerr_weakauth(transp);
177		return;
178	}
179
180	if (!rpc_gss_getcred(rqstp, &rcred, &ucred, NULL)) {
181		svcerr_systemerr(transp);
182		return;
183	}
184
185	printf("svc=%d, mech=%s, uid=%d, gid=%d, gids={",
186	    rcred->service, rcred->mechanism, ucred->uid, ucred->gid);
187	for (i = 0; i < ucred->gidlen; i++) {
188		if (i > 0) printf(",");
189		printf("%d", ucred->gidlist[i]);
190	}
191	printf("}\n");
192
193	switch (rqstp->rq_proc) {
194	case 0:
195		if (!svc_getargs(transp, (xdrproc_t) xdr_void, 0)) {
196			svcerr_decode(transp);
197			goto out;
198		}
199		if (!svc_sendreply(transp, (xdrproc_t) xdr_void, 0)) {
200			svcerr_systemerr(transp);
201		}
202		goto out;
203
204	case 1:
205		if (!svc_getargs(transp, (xdrproc_t) xdr_int,
206			(char *) &num)) {
207			svcerr_decode(transp);
208			goto out;
209		}
210		num += 100;
211		if (!svc_sendreply(transp, (xdrproc_t) xdr_int,
212			(char *) &num)) {
213			svcerr_systemerr(transp);
214		}
215		goto out;
216
217	default:
218		svcerr_noproc(transp);
219		goto out;
220	}
221
222out:
223	return;
224}
225
226#if 0
227static void
228report_error(gss_OID mech, OM_uint32 maj, OM_uint32 min)
229{
230	OM_uint32 maj_stat, min_stat;
231	OM_uint32 message_context;
232	gss_buffer_desc buf;
233
234	printf("major_stat=%d, minor_stat=%d\n", maj, min);
235
236	message_context = 0;
237	do {
238		maj_stat = gss_display_status(&min_stat, maj,
239		    GSS_C_GSS_CODE, GSS_C_NO_OID, &message_context, &buf);
240		printf("%.*s\n", (int)buf.length, (char *) buf.value);
241		gss_release_buffer(&min_stat, &buf);
242	} while (message_context);
243	if (mech) {
244		message_context = 0;
245		do {
246			maj_stat = gss_display_status(&min_stat, min,
247			    GSS_C_MECH_CODE, mech, &message_context, &buf);
248			printf("%.*s\n", (int)buf.length, (char *) buf.value);
249			gss_release_buffer(&min_stat, &buf);
250		} while (message_context);
251	}
252	exit(1);
253}
254#endif
255
256static bool_t
257server_new_context(__unused struct svc_req *req,
258    __unused gss_cred_id_t deleg,
259    __unused gss_ctx_id_t gss_context,
260    rpc_gss_lock_t *lock,
261    __unused void **cookie)
262{
263	rpc_gss_rawcred_t *rcred = lock->raw_cred;
264
265	printf("new security context version=%d, mech=%s, qop=%s:\n",
266	    rcred->version, rcred->mechanism, rcred->qop);
267	print_principal(rcred->client_principal);
268
269	if (!server_acl)
270		return (TRUE);
271
272	if (rcred->client_principal->len != server_acl->len
273	    || memcmp(rcred->client_principal->name, server_acl->name,
274		server_acl->len)) {
275		return (FALSE);
276	}
277
278	return (TRUE);
279}
280
281static void
282test_server(__unused int argc, __unused const char **argv)
283{
284	char hostname[128];
285	char principal[128 + 5];
286	const char **mechs;
287	static rpc_gss_callback_t cb;
288
289	if (argc == 3) {
290		if (!rpc_gss_get_principal_name(&server_acl, argv[1],
291			argv[2], NULL, NULL)) {
292			printf("Can't create %s ACL entry for %s\n",
293			    argv[1], argv[2]);
294			return;
295		}
296	}
297
298	gethostname(hostname, sizeof(hostname));;
299	snprintf(principal, sizeof(principal), "host@%s", hostname);
300
301	mechs = rpc_gss_get_mechanisms();
302	while (*mechs) {
303		if (!rpc_gss_set_svc_name(principal, *mechs, GSS_C_INDEFINITE,
304			123456, 1)) {
305			rpc_gss_error_t e;
306
307			rpc_gss_get_error(&e);
308			printf("setting name for %s for %s failed: %d, %d\n",
309			    principal, *mechs,
310			     e.rpc_gss_error, e.system_error);
311
312#if 0
313			gss_OID mech_oid;
314			gss_OID_set_desc oid_set;
315			gss_name_t name;
316			OM_uint32 maj_stat, min_stat;
317			gss_buffer_desc namebuf;
318			gss_cred_id_t cred;
319
320			rpc_gss_mech_to_oid(*mechs, &mech_oid);
321			oid_set.count = 1;
322			oid_set.elements = mech_oid;
323
324			namebuf.value = principal;
325			namebuf.length = strlen(principal);
326			maj_stat = gss_import_name(&min_stat, &namebuf,
327			    GSS_C_NT_HOSTBASED_SERVICE, &name);
328			if (maj_stat) {
329				printf("gss_import_name failed\n");
330				report_error(mech_oid, maj_stat, min_stat);
331			}
332			maj_stat = gss_acquire_cred(&min_stat, name,
333			    0, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL);
334			if (maj_stat) {
335				printf("gss_acquire_cred failed\n");
336				report_error(mech_oid, maj_stat, min_stat);
337			}
338#endif
339		}
340		mechs++;
341	}
342
343	cb.program = 123456;
344	cb.version = 1;
345	cb.callback = server_new_context;
346	rpc_gss_set_callback(&cb);
347
348	svc_create(server_program_1, 123456, 1, 0);
349	svc_run();
350}
351
352static void
353test_get_principal_name(int argc, const char **argv)
354{
355	const char *mechname, *name, *node, *domain;
356	rpc_gss_principal_t principal;
357
358	if (argc < 3 || argc > 5) {
359		printf("usage: rpctest principal <mechname> <name> "
360		    "[<node> [<domain>] ]\n");
361		exit(1);
362	}
363
364	mechname = argv[1];
365	name = argv[2];
366	node = NULL;
367	domain = NULL;
368	if (argc > 3) {
369		node = argv[3];
370		if (argc > 4)
371			domain = argv[4];
372	}
373
374	if (rpc_gss_get_principal_name(&principal, mechname, name,
375		node, domain)) {
376		printf("succeeded:\n");
377		print_principal(principal);
378		free(principal);
379	} else {
380		printf("failed\n");
381	}
382}
383
384int
385main(int argc, const char **argv)
386{
387
388	if (argc < 2)
389		usage();
390	if (!strcmp(argv[1], "client"))
391		test_client(argc - 1, argv + 1);
392	else if (!strcmp(argv[1], "server"))
393		test_server(argc - 1, argv + 1);
394	else if (!strcmp(argv[1], "principal"))
395		test_get_principal_name(argc - 1, argv + 1);
396	else
397		usage();
398
399	return (0);
400}
401