1133123Sjmg/*- 2133123Sjmg * Copyright (c) 2008 Doug Rabson 3133123Sjmg * All rights reserved. 4133123Sjmg * 5133123Sjmg * Redistribution and use in source and binary forms, with or without 6133123Sjmg * modification, are permitted provided that the following conditions 7133123Sjmg * are met: 8133123Sjmg * 1. Redistributions of source code must retain the above copyright 9133123Sjmg * notice, this list of conditions and the following disclaimer. 10133123Sjmg * 2. Redistributions in binary form must reproduce the above copyright 11133123Sjmg * notice, this list of conditions and the following disclaimer in the 12133123Sjmg * documentation and/or other materials provided with the distribution. 13133123Sjmg * 14133123Sjmg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15133123Sjmg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16133123Sjmg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17133123Sjmg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18133123Sjmg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19133123Sjmg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20133123Sjmg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21133123Sjmg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22133123Sjmg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23133123Sjmg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24133123Sjmg * SUCH DAMAGE. 25133123Sjmg * 26133123Sjmg * $FreeBSD$ 27133123Sjmg */ 28133123Sjmg 29133123Sjmg#include <ctype.h> 30133123Sjmg#include <stdio.h> 31133123Sjmg#include <string.h> 32133123Sjmg#include <stdlib.h> 33133123Sjmg#include <errno.h> 34143864Sjmg#include <sys/queue.h> 35133123Sjmg#include <rpc/rpc.h> 36133123Sjmg#include <rpc/rpcsec_gss.h> 37133123Sjmg 38133123Sjmg#include "rpcsec_gss_int.h" 39133123Sjmg 40133123Sjmg#ifndef _PATH_GSS_MECH 41133123Sjmg#define _PATH_GSS_MECH "/etc/gss/mech" 42133123Sjmg#endif 43133123Sjmg 44133123Sjmg#ifndef _PATH_GSS_QOP 45133123Sjmg#define _PATH_GSS_QOP "/etc/gss/qop" 46133123Sjmg#endif 47133123Sjmg 48133123Sjmgstruct mech_info { 49133123Sjmg SLIST_ENTRY(mech_info) link; 50133123Sjmg char *name; 51133123Sjmg gss_OID_desc oid; 52133123Sjmg const char **qops; 53133123Sjmg char *lib; 54133123Sjmg char *kobj; 55133123Sjmg}; 56133123SjmgSLIST_HEAD(mech_info_list, mech_info); 57133123Sjmg 58133123Sjmgstatic struct mech_info_list mechs = SLIST_HEAD_INITIALIZER(mechs); 59133123Sjmgstatic const char **mech_names; 60133123Sjmg 61133123Sjmgstruct qop_info { 62133123Sjmg SLIST_ENTRY(qop_info) link; 63133123Sjmg char *name; 64143864Sjmg char* mech; 65133123Sjmg u_int qop; 66133123Sjmg}; 67133123SjmgSLIST_HEAD(qop_info_list, qop_info); 68133123Sjmg 69133123Sjmgstatic struct qop_info_list qops = SLIST_HEAD_INITIALIZER(qops); 70133123Sjmg 71228975Suqsstatic int 72133123Sjmg_rpc_gss_string_to_oid(const char* s, gss_OID oid) 73228975Suqs{ 74133123Sjmg int number_count, i, j; 75133123Sjmg int byte_count; 76133123Sjmg const char *p, *q; 77133123Sjmg char *res; 78133123Sjmg 79133123Sjmg /* 80133123Sjmg * First figure out how many numbers in the oid, then 81133123Sjmg * calculate the compiled oid size. 82133123Sjmg */ 83133123Sjmg number_count = 0; 84133123Sjmg for (p = s; p; p = q) { 85133123Sjmg q = strchr(p, '.'); 86133123Sjmg if (q) q = q + 1; 87133123Sjmg number_count++; 88133123Sjmg } 89133123Sjmg 90228975Suqs /* 91133123Sjmg * The first two numbers are in the first byte and each 92133123Sjmg * subsequent number is encoded in a variable byte sequence. 93133123Sjmg */ 94133123Sjmg if (number_count < 2) 95133123Sjmg return (EINVAL); 96133123Sjmg 97133123Sjmg /* 98133123Sjmg * We do this in two passes. The first pass, we just figure 99133123Sjmg * out the size. Second time around, we actually encode the 100133123Sjmg * number. 101133123Sjmg */ 102133123Sjmg res = 0; 103133123Sjmg for (i = 0; i < 2; i++) { 104133123Sjmg byte_count = 0; 105133123Sjmg for (p = s, j = 0; p; p = q, j++) { 106133123Sjmg u_int number = 0; 107133123Sjmg 108133123Sjmg /* 109133123Sjmg * Find the end of this number. 110133123Sjmg */ 111133123Sjmg q = strchr(p, '.'); 112133123Sjmg if (q) q = q + 1; 113133123Sjmg 114133123Sjmg /* 115133123Sjmg * Read the number of of the string. Don't 116133123Sjmg * bother with anything except base ten. 117133123Sjmg */ 118133123Sjmg while (*p && *p != '.') { 119133123Sjmg number = 10 * number + (*p - '0'); 120133123Sjmg p++; 121133123Sjmg } 122133123Sjmg 123133123Sjmg /* 124133123Sjmg * Encode the number. The first two numbers 125133123Sjmg * are packed into the first byte. Subsequent 126133123Sjmg * numbers are encoded in bytes seven bits at 127133123Sjmg * a time with the last byte having the high 128133123Sjmg * bit set. 129133123Sjmg */ 130133123Sjmg if (j == 0) { 131133123Sjmg if (res) 132133123Sjmg *res = number * 40; 133133123Sjmg } else if (j == 1) { 134133123Sjmg if (res) { 135133123Sjmg *res += number; 136133123Sjmg res++; 137133123Sjmg } 138133123Sjmg byte_count++; 139133123Sjmg } else if (j >= 2) { 140133123Sjmg /* 141133123Sjmg * The number is encoded in seven bit chunks. 142133123Sjmg */ 143 u_int t; 144 int bytes; 145 146 bytes = 0; 147 for (t = number; t; t >>= 7) 148 bytes++; 149 if (bytes == 0) bytes = 1; 150 while (bytes) { 151 if (res) { 152 int bit = 7*(bytes-1); 153 154 *res = (number >> bit) & 0x7f; 155 if (bytes != 1) 156 *res |= 0x80; 157 res++; 158 } 159 byte_count++; 160 bytes--; 161 } 162 } 163 } 164 if (!res) { 165 res = malloc(byte_count); 166 if (!res) 167 return (ENOMEM); 168 oid->length = byte_count; 169 oid->elements = res; 170 } 171 } 172 173 return (0); 174} 175 176static void 177_rpc_gss_load_mech(void) 178{ 179 FILE *fp; 180 char buf[256]; 181 char *p; 182 char *name, *oid, *lib, *kobj; 183 struct mech_info *info; 184 int count; 185 const char **pp; 186 187 if (SLIST_FIRST(&mechs)) 188 return; 189 190 fp = fopen(_PATH_GSS_MECH, "r"); 191 if (!fp) 192 return; 193 194 count = 0; 195 while (fgets(buf, sizeof(buf), fp)) { 196 if (*buf == '#') 197 continue; 198 p = buf; 199 name = strsep(&p, "\t\n "); 200 if (p) while (isspace(*p)) p++; 201 oid = strsep(&p, "\t\n "); 202 if (p) while (isspace(*p)) p++; 203 lib = strsep(&p, "\t\n "); 204 if (p) while (isspace(*p)) p++; 205 kobj = strsep(&p, "\t\n "); 206 if (!name || !oid || !lib || !kobj) 207 continue; 208 209 info = malloc(sizeof(struct mech_info)); 210 if (!info) 211 break; 212 if (_rpc_gss_string_to_oid(oid, &info->oid)) { 213 free(info); 214 continue; 215 } 216 info->name = strdup(name); 217 info->qops = NULL; 218 info->lib = strdup(lib); 219 info->kobj = strdup(kobj); 220 SLIST_INSERT_HEAD(&mechs, info, link); 221 count++; 222 } 223 fclose(fp); 224 225 mech_names = malloc((count + 1) * sizeof(char*)); 226 pp = mech_names; 227 SLIST_FOREACH(info, &mechs, link) { 228 *pp++ = info->name; 229 } 230 *pp = NULL; 231} 232 233static void 234_rpc_gss_load_qop(void) 235{ 236 FILE *fp; 237 char buf[256]; 238 char *p; 239 char *name, *num, *mech; 240 struct mech_info *minfo; 241 struct qop_info *info; 242 int count; 243 const char **mech_qops; 244 const char **pp; 245 246 if (SLIST_FIRST(&qops)) 247 return; 248 249 fp = fopen(_PATH_GSS_QOP, "r"); 250 if (!fp) 251 return; 252 253 while (fgets(buf, sizeof(buf), fp)) { 254 if (*buf == '#') 255 continue; 256 p = buf; 257 name = strsep(&p, "\t\n "); 258 if (p) while (isspace(*p)) p++; 259 num = strsep(&p, "\t\n "); 260 if (p) while (isspace(*p)) p++; 261 mech = strsep(&p, "\t\n "); 262 if (!name || !num || !mech) 263 continue; 264 265 info = malloc(sizeof(struct qop_info)); 266 if (!info) 267 break; 268 info->name = strdup(name); 269 info->qop = strtoul(name, 0, 0); 270 info->mech = strdup(mech); 271 SLIST_INSERT_HEAD(&qops, info, link); 272 } 273 fclose(fp); 274 275 /* 276 * Compile lists of qops for each mechanism. 277 */ 278 SLIST_FOREACH(minfo, &mechs, link) { 279 count = 0; 280 SLIST_FOREACH(info, &qops, link) { 281 if (strcmp(info->mech, minfo->name) == 0) 282 count++; 283 } 284 mech_qops = malloc((count + 1) * sizeof(char*)); 285 pp = mech_qops; 286 SLIST_FOREACH(info, &qops, link) { 287 if (strcmp(info->mech, minfo->name) == 0) 288 *pp++ = info->name; 289 } 290 *pp = NULL; 291 minfo->qops = mech_qops; 292 } 293} 294 295bool_t 296rpc_gss_mech_to_oid(const char *mech, gss_OID *oid_ret) 297{ 298 struct mech_info *info; 299 300 _rpc_gss_load_mech(); 301 SLIST_FOREACH(info, &mechs, link) { 302 if (!strcmp(info->name, mech)) { 303 *oid_ret = &info->oid; 304 return (TRUE); 305 } 306 } 307 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 308 return (FALSE); 309} 310 311bool_t 312rpc_gss_oid_to_mech(gss_OID oid, const char **mech_ret) 313{ 314 struct mech_info *info; 315 316 _rpc_gss_load_mech(); 317 SLIST_FOREACH(info, &mechs, link) { 318 if (oid->length == info->oid.length 319 && !memcmp(oid->elements, info->oid.elements, 320 oid->length)) { 321 *mech_ret = info->name; 322 return (TRUE); 323 } 324 } 325 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 326 return (FALSE); 327} 328 329bool_t 330rpc_gss_qop_to_num(const char *qop, const char *mech, u_int *num_ret) 331{ 332 struct qop_info *info; 333 334 _rpc_gss_load_qop(); 335 SLIST_FOREACH(info, &qops, link) { 336 if (strcmp(info->name, qop) == 0 337 && strcmp(info->mech, mech) == 0) { 338 *num_ret = info->qop; 339 return (TRUE); 340 } 341 } 342 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 343 return (FALSE); 344} 345 346const char * 347_rpc_gss_num_to_qop(const char *mech, u_int num) 348{ 349 struct qop_info *info; 350 351 if (num == GSS_C_QOP_DEFAULT) 352 return "default"; 353 354 _rpc_gss_load_qop(); 355 SLIST_FOREACH(info, &qops, link) { 356 if (info->qop == num && strcmp(info->mech, mech) == 0) { 357 return (info->name); 358 } 359 } 360 return (NULL); 361} 362 363const char ** 364rpc_gss_get_mechanisms(void) 365{ 366 367 _rpc_gss_load_mech(); 368 return (mech_names); 369} 370 371const char ** 372rpc_gss_get_mech_info(const char *mech, rpc_gss_service_t *service) 373{ 374 struct mech_info *info; 375 376 _rpc_gss_load_mech(); 377 _rpc_gss_load_qop(); 378 SLIST_FOREACH(info, &mechs, link) { 379 if (!strcmp(mech, info->name)) { 380 /* 381 * I'm not sure what to do with service 382 * here. The Solaris manpages are not clear on 383 * the subject and the OpenSolaris code just 384 * sets it to rpc_gss_svc_privacy 385 * unconditionally with a comment noting that 386 * it is bogus. 387 */ 388 *service = rpc_gss_svc_privacy; 389 return info->qops; 390 } 391 } 392 393 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 394 return (NULL); 395} 396 397bool_t 398rpc_gss_get_versions(u_int *vers_hi, u_int *vers_lo) 399{ 400 401 *vers_hi = 1; 402 *vers_lo = 1; 403 return (TRUE); 404} 405 406bool_t 407rpc_gss_is_installed(const char *mech) 408{ 409 struct mech_info *info; 410 411 _rpc_gss_load_mech(); 412 SLIST_FOREACH(info, &mechs, link) 413 if (!strcmp(mech, info->name)) 414 return (TRUE); 415 return (FALSE); 416} 417 418