1181344Sdfr/*- 2181344Sdfr * Copyright (c) 2008 Doug Rabson 3181344Sdfr * All rights reserved. 4181344Sdfr * 5181344Sdfr * Redistribution and use in source and binary forms, with or without 6181344Sdfr * modification, are permitted provided that the following conditions 7181344Sdfr * are met: 8181344Sdfr * 1. Redistributions of source code must retain the above copyright 9181344Sdfr * notice, this list of conditions and the following disclaimer. 10181344Sdfr * 2. Redistributions in binary form must reproduce the above copyright 11181344Sdfr * notice, this list of conditions and the following disclaimer in the 12181344Sdfr * documentation and/or other materials provided with the distribution. 13181344Sdfr * 14181344Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15181344Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16181344Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17181344Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18181344Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19181344Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20181344Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21181344Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22181344Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23181344Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24181344Sdfr * SUCH DAMAGE. 25181344Sdfr * 26181344Sdfr * $FreeBSD$ 27181344Sdfr */ 28181344Sdfr 29181344Sdfr#include <ctype.h> 30181344Sdfr#include <stdio.h> 31181344Sdfr#include <string.h> 32181344Sdfr#include <stdlib.h> 33181344Sdfr#include <errno.h> 34181344Sdfr#include <sys/queue.h> 35181344Sdfr#include <rpc/rpc.h> 36181344Sdfr#include <rpc/rpcsec_gss.h> 37181344Sdfr 38181344Sdfr#include "rpcsec_gss_int.h" 39181344Sdfr 40181344Sdfr#ifndef _PATH_GSS_MECH 41181344Sdfr#define _PATH_GSS_MECH "/etc/gss/mech" 42181344Sdfr#endif 43181344Sdfr 44181344Sdfr#ifndef _PATH_GSS_QOP 45181344Sdfr#define _PATH_GSS_QOP "/etc/gss/qop" 46181344Sdfr#endif 47181344Sdfr 48181344Sdfrstruct mech_info { 49181344Sdfr SLIST_ENTRY(mech_info) link; 50181344Sdfr char *name; 51181344Sdfr gss_OID_desc oid; 52181344Sdfr const char **qops; 53181344Sdfr char *lib; 54181344Sdfr char *kobj; 55181344Sdfr}; 56181344SdfrSLIST_HEAD(mech_info_list, mech_info); 57181344Sdfr 58201145Santoinestatic struct mech_info_list mechs = SLIST_HEAD_INITIALIZER(mechs); 59181344Sdfrstatic const char **mech_names; 60181344Sdfr 61181344Sdfrstruct qop_info { 62181344Sdfr SLIST_ENTRY(qop_info) link; 63181344Sdfr char *name; 64181344Sdfr char* mech; 65181344Sdfr u_int qop; 66181344Sdfr}; 67181344SdfrSLIST_HEAD(qop_info_list, qop_info); 68181344Sdfr 69201145Santoinestatic struct qop_info_list qops = SLIST_HEAD_INITIALIZER(qops); 70181344Sdfr 71181344Sdfrstatic int 72181344Sdfr_rpc_gss_string_to_oid(const char* s, gss_OID oid) 73181344Sdfr{ 74181344Sdfr int number_count, i, j; 75181344Sdfr int byte_count; 76181344Sdfr const char *p, *q; 77181344Sdfr char *res; 78181344Sdfr 79181344Sdfr /* 80181344Sdfr * First figure out how many numbers in the oid, then 81181344Sdfr * calculate the compiled oid size. 82181344Sdfr */ 83181344Sdfr number_count = 0; 84181344Sdfr for (p = s; p; p = q) { 85181344Sdfr q = strchr(p, '.'); 86181344Sdfr if (q) q = q + 1; 87181344Sdfr number_count++; 88181344Sdfr } 89181344Sdfr 90181344Sdfr /* 91181344Sdfr * The first two numbers are in the first byte and each 92181344Sdfr * subsequent number is encoded in a variable byte sequence. 93181344Sdfr */ 94181344Sdfr if (number_count < 2) 95181344Sdfr return (EINVAL); 96181344Sdfr 97181344Sdfr /* 98181344Sdfr * We do this in two passes. The first pass, we just figure 99181344Sdfr * out the size. Second time around, we actually encode the 100181344Sdfr * number. 101181344Sdfr */ 102181344Sdfr res = 0; 103181344Sdfr for (i = 0; i < 2; i++) { 104181344Sdfr byte_count = 0; 105181344Sdfr for (p = s, j = 0; p; p = q, j++) { 106181344Sdfr u_int number = 0; 107181344Sdfr 108181344Sdfr /* 109181344Sdfr * Find the end of this number. 110181344Sdfr */ 111181344Sdfr q = strchr(p, '.'); 112181344Sdfr if (q) q = q + 1; 113181344Sdfr 114181344Sdfr /* 115181344Sdfr * Read the number of of the string. Don't 116181344Sdfr * bother with anything except base ten. 117181344Sdfr */ 118181344Sdfr while (*p && *p != '.') { 119181344Sdfr number = 10 * number + (*p - '0'); 120181344Sdfr p++; 121181344Sdfr } 122181344Sdfr 123181344Sdfr /* 124181344Sdfr * Encode the number. The first two numbers 125181344Sdfr * are packed into the first byte. Subsequent 126181344Sdfr * numbers are encoded in bytes seven bits at 127181344Sdfr * a time with the last byte having the high 128181344Sdfr * bit set. 129181344Sdfr */ 130181344Sdfr if (j == 0) { 131181344Sdfr if (res) 132181344Sdfr *res = number * 40; 133181344Sdfr } else if (j == 1) { 134181344Sdfr if (res) { 135181344Sdfr *res += number; 136181344Sdfr res++; 137181344Sdfr } 138181344Sdfr byte_count++; 139181344Sdfr } else if (j >= 2) { 140181344Sdfr /* 141181344Sdfr * The number is encoded in seven bit chunks. 142181344Sdfr */ 143181344Sdfr u_int t; 144181344Sdfr int bytes; 145181344Sdfr 146181344Sdfr bytes = 0; 147181344Sdfr for (t = number; t; t >>= 7) 148181344Sdfr bytes++; 149181344Sdfr if (bytes == 0) bytes = 1; 150181344Sdfr while (bytes) { 151181344Sdfr if (res) { 152181344Sdfr int bit = 7*(bytes-1); 153181344Sdfr 154181344Sdfr *res = (number >> bit) & 0x7f; 155181344Sdfr if (bytes != 1) 156181344Sdfr *res |= 0x80; 157181344Sdfr res++; 158181344Sdfr } 159181344Sdfr byte_count++; 160181344Sdfr bytes--; 161181344Sdfr } 162181344Sdfr } 163181344Sdfr } 164181344Sdfr if (!res) { 165181344Sdfr res = malloc(byte_count); 166181344Sdfr if (!res) 167181344Sdfr return (ENOMEM); 168181344Sdfr oid->length = byte_count; 169181344Sdfr oid->elements = res; 170181344Sdfr } 171181344Sdfr } 172181344Sdfr 173181344Sdfr return (0); 174181344Sdfr} 175181344Sdfr 176181344Sdfrstatic void 177181344Sdfr_rpc_gss_load_mech(void) 178181344Sdfr{ 179181344Sdfr FILE *fp; 180181344Sdfr char buf[256]; 181181344Sdfr char *p; 182181344Sdfr char *name, *oid, *lib, *kobj; 183181344Sdfr struct mech_info *info; 184181344Sdfr int count; 185181344Sdfr const char **pp; 186181344Sdfr 187181344Sdfr if (SLIST_FIRST(&mechs)) 188181344Sdfr return; 189181344Sdfr 190181344Sdfr fp = fopen(_PATH_GSS_MECH, "r"); 191181344Sdfr if (!fp) 192181344Sdfr return; 193181344Sdfr 194181344Sdfr count = 0; 195181344Sdfr while (fgets(buf, sizeof(buf), fp)) { 196181344Sdfr if (*buf == '#') 197181344Sdfr continue; 198181344Sdfr p = buf; 199181344Sdfr name = strsep(&p, "\t\n "); 200181344Sdfr if (p) while (isspace(*p)) p++; 201181344Sdfr oid = strsep(&p, "\t\n "); 202181344Sdfr if (p) while (isspace(*p)) p++; 203181344Sdfr lib = strsep(&p, "\t\n "); 204181344Sdfr if (p) while (isspace(*p)) p++; 205181344Sdfr kobj = strsep(&p, "\t\n "); 206181344Sdfr if (!name || !oid || !lib || !kobj) 207181344Sdfr continue; 208181344Sdfr 209181344Sdfr info = malloc(sizeof(struct mech_info)); 210181344Sdfr if (!info) 211181344Sdfr break; 212181344Sdfr if (_rpc_gss_string_to_oid(oid, &info->oid)) { 213181344Sdfr free(info); 214181344Sdfr continue; 215181344Sdfr } 216181344Sdfr info->name = strdup(name); 217181344Sdfr info->qops = NULL; 218181344Sdfr info->lib = strdup(lib); 219181344Sdfr info->kobj = strdup(kobj); 220181344Sdfr SLIST_INSERT_HEAD(&mechs, info, link); 221181344Sdfr count++; 222181344Sdfr } 223181344Sdfr fclose(fp); 224181344Sdfr 225181344Sdfr mech_names = malloc((count + 1) * sizeof(char*)); 226181344Sdfr pp = mech_names; 227181344Sdfr SLIST_FOREACH(info, &mechs, link) { 228181344Sdfr *pp++ = info->name; 229181344Sdfr } 230181344Sdfr *pp = NULL; 231181344Sdfr} 232181344Sdfr 233181344Sdfrstatic void 234181344Sdfr_rpc_gss_load_qop(void) 235181344Sdfr{ 236181344Sdfr FILE *fp; 237181344Sdfr char buf[256]; 238181344Sdfr char *p; 239181344Sdfr char *name, *num, *mech; 240181344Sdfr struct mech_info *minfo; 241181344Sdfr struct qop_info *info; 242181344Sdfr int count; 243181344Sdfr const char **mech_qops; 244181344Sdfr const char **pp; 245181344Sdfr 246181344Sdfr if (SLIST_FIRST(&qops)) 247181344Sdfr return; 248181344Sdfr 249181344Sdfr fp = fopen(_PATH_GSS_QOP, "r"); 250181344Sdfr if (!fp) 251181344Sdfr return; 252181344Sdfr 253181344Sdfr while (fgets(buf, sizeof(buf), fp)) { 254181344Sdfr if (*buf == '#') 255181344Sdfr continue; 256181344Sdfr p = buf; 257181344Sdfr name = strsep(&p, "\t\n "); 258181344Sdfr if (p) while (isspace(*p)) p++; 259181344Sdfr num = strsep(&p, "\t\n "); 260181344Sdfr if (p) while (isspace(*p)) p++; 261181344Sdfr mech = strsep(&p, "\t\n "); 262181344Sdfr if (!name || !num || !mech) 263181344Sdfr continue; 264181344Sdfr 265181344Sdfr info = malloc(sizeof(struct qop_info)); 266181344Sdfr if (!info) 267181344Sdfr break; 268181344Sdfr info->name = strdup(name); 269181344Sdfr info->qop = strtoul(name, 0, 0); 270181344Sdfr info->mech = strdup(mech); 271181344Sdfr SLIST_INSERT_HEAD(&qops, info, link); 272181344Sdfr } 273181344Sdfr fclose(fp); 274181344Sdfr 275181344Sdfr /* 276181344Sdfr * Compile lists of qops for each mechanism. 277181344Sdfr */ 278181344Sdfr SLIST_FOREACH(minfo, &mechs, link) { 279181344Sdfr count = 0; 280181344Sdfr SLIST_FOREACH(info, &qops, link) { 281181344Sdfr if (strcmp(info->mech, minfo->name) == 0) 282181344Sdfr count++; 283181344Sdfr } 284181344Sdfr mech_qops = malloc((count + 1) * sizeof(char*)); 285181344Sdfr pp = mech_qops; 286181344Sdfr SLIST_FOREACH(info, &qops, link) { 287181344Sdfr if (strcmp(info->mech, minfo->name) == 0) 288181344Sdfr *pp++ = info->name; 289181344Sdfr } 290181344Sdfr *pp = NULL; 291181344Sdfr minfo->qops = mech_qops; 292181344Sdfr } 293181344Sdfr} 294181344Sdfr 295181344Sdfrbool_t 296181344Sdfrrpc_gss_mech_to_oid(const char *mech, gss_OID *oid_ret) 297181344Sdfr{ 298181344Sdfr struct mech_info *info; 299181344Sdfr 300181344Sdfr _rpc_gss_load_mech(); 301181344Sdfr SLIST_FOREACH(info, &mechs, link) { 302181344Sdfr if (!strcmp(info->name, mech)) { 303181344Sdfr *oid_ret = &info->oid; 304181344Sdfr return (TRUE); 305181344Sdfr } 306181344Sdfr } 307181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 308181344Sdfr return (FALSE); 309181344Sdfr} 310181344Sdfr 311181344Sdfrbool_t 312181344Sdfrrpc_gss_oid_to_mech(gss_OID oid, const char **mech_ret) 313181344Sdfr{ 314181344Sdfr struct mech_info *info; 315181344Sdfr 316181344Sdfr _rpc_gss_load_mech(); 317181344Sdfr SLIST_FOREACH(info, &mechs, link) { 318181344Sdfr if (oid->length == info->oid.length 319181344Sdfr && !memcmp(oid->elements, info->oid.elements, 320181344Sdfr oid->length)) { 321181344Sdfr *mech_ret = info->name; 322181344Sdfr return (TRUE); 323181344Sdfr } 324181344Sdfr } 325181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 326181344Sdfr return (FALSE); 327181344Sdfr} 328181344Sdfr 329181344Sdfrbool_t 330181344Sdfrrpc_gss_qop_to_num(const char *qop, const char *mech, u_int *num_ret) 331181344Sdfr{ 332181344Sdfr struct qop_info *info; 333181344Sdfr 334181344Sdfr _rpc_gss_load_qop(); 335181344Sdfr SLIST_FOREACH(info, &qops, link) { 336181344Sdfr if (strcmp(info->name, qop) == 0 337181344Sdfr && strcmp(info->mech, mech) == 0) { 338181344Sdfr *num_ret = info->qop; 339181344Sdfr return (TRUE); 340181344Sdfr } 341181344Sdfr } 342181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 343181344Sdfr return (FALSE); 344181344Sdfr} 345181344Sdfr 346181344Sdfrconst char * 347181344Sdfr_rpc_gss_num_to_qop(const char *mech, u_int num) 348181344Sdfr{ 349181344Sdfr struct qop_info *info; 350181344Sdfr 351181344Sdfr if (num == GSS_C_QOP_DEFAULT) 352181344Sdfr return "default"; 353181344Sdfr 354181344Sdfr _rpc_gss_load_qop(); 355181344Sdfr SLIST_FOREACH(info, &qops, link) { 356181344Sdfr if (info->qop == num && strcmp(info->mech, mech) == 0) { 357181344Sdfr return (info->name); 358181344Sdfr } 359181344Sdfr } 360181344Sdfr return (NULL); 361181344Sdfr} 362181344Sdfr 363181344Sdfrconst char ** 364181344Sdfrrpc_gss_get_mechanisms(void) 365181344Sdfr{ 366181344Sdfr 367181344Sdfr _rpc_gss_load_mech(); 368181344Sdfr return (mech_names); 369181344Sdfr} 370181344Sdfr 371181344Sdfrconst char ** 372181344Sdfrrpc_gss_get_mech_info(const char *mech, rpc_gss_service_t *service) 373181344Sdfr{ 374181344Sdfr struct mech_info *info; 375181344Sdfr 376181344Sdfr _rpc_gss_load_mech(); 377181344Sdfr _rpc_gss_load_qop(); 378181344Sdfr SLIST_FOREACH(info, &mechs, link) { 379181344Sdfr if (!strcmp(mech, info->name)) { 380181344Sdfr /* 381181344Sdfr * I'm not sure what to do with service 382181344Sdfr * here. The Solaris manpages are not clear on 383181344Sdfr * the subject and the OpenSolaris code just 384181344Sdfr * sets it to rpc_gss_svc_privacy 385181344Sdfr * unconditionally with a comment noting that 386181344Sdfr * it is bogus. 387181344Sdfr */ 388181344Sdfr *service = rpc_gss_svc_privacy; 389181344Sdfr return info->qops; 390181344Sdfr } 391181344Sdfr } 392181344Sdfr 393181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 394181344Sdfr return (NULL); 395181344Sdfr} 396181344Sdfr 397181344Sdfrbool_t 398181344Sdfrrpc_gss_get_versions(u_int *vers_hi, u_int *vers_lo) 399181344Sdfr{ 400181344Sdfr 401181344Sdfr *vers_hi = 1; 402181344Sdfr *vers_lo = 1; 403181344Sdfr return (TRUE); 404181344Sdfr} 405181344Sdfr 406181344Sdfrbool_t 407181344Sdfrrpc_gss_is_installed(const char *mech) 408181344Sdfr{ 409181344Sdfr struct mech_info *info; 410181344Sdfr 411181344Sdfr _rpc_gss_load_mech(); 412181344Sdfr SLIST_FOREACH(info, &mechs, link) 413181344Sdfr if (!strcmp(mech, info->name)) 414181344Sdfr return (TRUE); 415181344Sdfr return (FALSE); 416181344Sdfr} 417181344Sdfr 418