1 /*- 2 * Copyright (c) 2008 Doug Rabson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <ctype.h> 30 #include <stdio.h> 31 #include <string.h> 32 #include <stdlib.h> 33 #include <errno.h> 34 #include <sys/queue.h> 35 #include <rpc/rpc.h> 36 #include <rpc/rpcsec_gss.h> 37 38 #include "rpcsec_gss_int.h" 39 40 #ifndef _PATH_GSS_MECH 41 #define _PATH_GSS_MECH "/etc/gss/mech" 42 #endif 43 44 #ifndef _PATH_GSS_QOP 45 #define _PATH_GSS_QOP "/etc/gss/qop" 46 #endif 47 48 struct mech_info { 49 SLIST_ENTRY(mech_info) link; 50 char *name; 51 gss_OID_desc oid; 52 const char **qops; 53 char *lib; 54 char *kobj; 55 }; 56 SLIST_HEAD(mech_info_list, mech_info); 57 58 static struct mech_info_list mechs = SLIST_HEAD_INITIALIZER(mechs); 59 static const char **mech_names; 60 61 struct qop_info { 62 SLIST_ENTRY(qop_info) link; 63 char *name; 64 char* mech; 65 u_int qop; 66 }; 67 SLIST_HEAD(qop_info_list, qop_info); 68 69 static struct qop_info_list qops = SLIST_HEAD_INITIALIZER(qops); 70 71 static int 72 _rpc_gss_string_to_oid(const char* s, gss_OID oid) 73 { 74 int number_count, i, j; 75 int byte_count; 76 const char *p, *q; 77 char *res; 78 79 /* 80 * First figure out how many numbers in the oid, then 81 * calculate the compiled oid size. 82 */ 83 number_count = 0; 84 for (p = s; p; p = q) { 85 q = strchr(p, '.'); 86 if (q) q = q + 1; 87 number_count++; 88 } 89 90 /* 91 * The first two numbers are in the first byte and each 92 * subsequent number is encoded in a variable byte sequence. 93 */ 94 if (number_count < 2) 95 return (EINVAL); 96 97 /* 98 * We do this in two passes. The first pass, we just figure 99 * out the size. Second time around, we actually encode the 100 * number. 101 */ 102 res = 0; 103 for (i = 0; i < 2; i++) { 104 byte_count = 0; 105 for (p = s, j = 0; p; p = q, j++) { 106 u_int number = 0; 107 108 /* 109 * Find the end of this number. 110 */ 111 q = strchr(p, '.'); 112 if (q) q = q + 1; 113 114 /* 115 * Read the number of of the string. Don't 116 * bother with anything except base ten. 117 */ 118 while (*p && *p != '.') { 119 number = 10 * number + (*p - '0'); 120 p++; 121 } 122 123 /* 124 * Encode the number. The first two numbers 125 * are packed into the first byte. Subsequent 126 * numbers are encoded in bytes seven bits at 127 * a time with the last byte having the high 128 * bit set. 129 */ 130 if (j == 0) { 131 if (res) 132 *res = number * 40; 133 } else if (j == 1) { 134 if (res) { 135 *res += number; 136 res++; 137 } 138 byte_count++; 139 } else if (j >= 2) { 140 /* 141 * The number is encoded in seven bit chunks. 142 */ 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 176 static 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 233 static 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 295 bool_t 296 rpc_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 311 bool_t 312 rpc_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 329 bool_t 330 rpc_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 346 const 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 363 const char ** 364 rpc_gss_get_mechanisms(void) 365 { 366 367 _rpc_gss_load_mech(); 368 return (mech_names); 369 } 370 371 const char ** 372 rpc_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 397 bool_t 398 rpc_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 406 bool_t 407 rpc_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