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 
47 static rpc_gss_principal_t server_acl = NULL;
48 
49 static void
50 usage(void)
51 {
52 	printf("rpctest client | server\n");
53 	exit(1);
54 }
55 
56 static void
57 print_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 
81 static void
82 test_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 
168 static void
169 server_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 
222 out:
223 	return;
224 }
225 
226 #if 0
227 static void
228 report_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 
256 static bool_t
257 server_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 
281 static void
282 test_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 
352 static void
353 test_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 
384 int
385 main(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