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