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