1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2008 Doug Rabson 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <ctype.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <stdlib.h> 35 #include <errno.h> 36 #include <sys/queue.h> 37 #include <rpc/rpc.h> 38 #include <rpc/rpcsec_gss.h> 39 40 #include "rpcsec_gss_int.h" 41 42 #ifndef _PATH_GSS_MECH 43 #define _PATH_GSS_MECH "/etc/gss/mech" 44 #endif 45 46 #ifndef _PATH_GSS_QOP 47 #define _PATH_GSS_QOP "/etc/gss/qop" 48 #endif 49 50 struct mech_info { 51 SLIST_ENTRY(mech_info) link; 52 char *name; 53 gss_OID_desc oid; 54 const char **qops; 55 char *lib; 56 char *kobj; 57 }; 58 SLIST_HEAD(mech_info_list, mech_info); 59 60 static struct mech_info_list mechs = SLIST_HEAD_INITIALIZER(mechs); 61 static const char **mech_names; 62 63 struct qop_info { 64 SLIST_ENTRY(qop_info) link; 65 char *name; 66 char* mech; 67 u_int qop; 68 }; 69 SLIST_HEAD(qop_info_list, qop_info); 70 71 static struct qop_info_list qops = SLIST_HEAD_INITIALIZER(qops); 72 73 static int 74 _rpc_gss_string_to_oid(const char* s, gss_OID oid) 75 { 76 int number_count, i, j; 77 int byte_count; 78 const char *p, *q; 79 char *res; 80 81 /* 82 * First figure out how many numbers in the oid, then 83 * calculate the compiled oid size. 84 */ 85 number_count = 0; 86 for (p = s; p; p = q) { 87 q = strchr(p, '.'); 88 if (q) q = q + 1; 89 number_count++; 90 } 91 92 /* 93 * The first two numbers are in the first byte and each 94 * subsequent number is encoded in a variable byte sequence. 95 */ 96 if (number_count < 2) 97 return (EINVAL); 98 99 /* 100 * We do this in two passes. The first pass, we just figure 101 * out the size. Second time around, we actually encode the 102 * number. 103 */ 104 res = 0; 105 for (i = 0; i < 2; i++) { 106 byte_count = 0; 107 for (p = s, j = 0; p; p = q, j++) { 108 u_int number = 0; 109 110 /* 111 * Find the end of this number. 112 */ 113 q = strchr(p, '.'); 114 if (q) q = q + 1; 115 116 /* 117 * Read the number of of the string. Don't 118 * bother with anything except base ten. 119 */ 120 while (*p && *p != '.') { 121 number = 10 * number + (*p - '0'); 122 p++; 123 } 124 125 /* 126 * Encode the number. The first two numbers 127 * are packed into the first byte. Subsequent 128 * numbers are encoded in bytes seven bits at 129 * a time with the last byte having the high 130 * bit set. 131 */ 132 if (j == 0) { 133 if (res) 134 *res = number * 40; 135 } else if (j == 1) { 136 if (res) { 137 *res += number; 138 res++; 139 } 140 byte_count++; 141 } else if (j >= 2) { 142 /* 143 * The number is encoded in seven bit chunks. 144 */ 145 u_int t; 146 int bytes; 147 148 bytes = 0; 149 for (t = number; t; t >>= 7) 150 bytes++; 151 if (bytes == 0) bytes = 1; 152 while (bytes) { 153 if (res) { 154 int bit = 7*(bytes-1); 155 156 *res = (number >> bit) & 0x7f; 157 if (bytes != 1) 158 *res |= 0x80; 159 res++; 160 } 161 byte_count++; 162 bytes--; 163 } 164 } 165 } 166 if (!res) { 167 res = malloc(byte_count); 168 if (!res) 169 return (ENOMEM); 170 oid->length = byte_count; 171 oid->elements = res; 172 } 173 } 174 175 return (0); 176 } 177 178 static void 179 _rpc_gss_load_mech(void) 180 { 181 FILE *fp; 182 char buf[256]; 183 char *p; 184 char *name, *oid, *lib, *kobj; 185 struct mech_info *info; 186 int count; 187 const char **pp; 188 189 if (SLIST_FIRST(&mechs)) 190 return; 191 192 fp = fopen(_PATH_GSS_MECH, "r"); 193 if (!fp) 194 return; 195 196 count = 0; 197 while (fgets(buf, sizeof(buf), fp)) { 198 if (*buf == '#') 199 continue; 200 p = buf; 201 name = strsep(&p, "\t\n "); 202 if (p) while (isspace(*p)) p++; 203 oid = strsep(&p, "\t\n "); 204 if (p) while (isspace(*p)) p++; 205 lib = strsep(&p, "\t\n "); 206 if (p) while (isspace(*p)) p++; 207 kobj = strsep(&p, "\t\n "); 208 if (!name || !oid || !lib || !kobj) 209 continue; 210 211 info = malloc(sizeof(struct mech_info)); 212 if (!info) 213 break; 214 if (_rpc_gss_string_to_oid(oid, &info->oid)) { 215 free(info); 216 continue; 217 } 218 info->name = strdup(name); 219 info->qops = NULL; 220 info->lib = strdup(lib); 221 info->kobj = strdup(kobj); 222 SLIST_INSERT_HEAD(&mechs, info, link); 223 count++; 224 } 225 fclose(fp); 226 227 mech_names = malloc((count + 1) * sizeof(char*)); 228 pp = mech_names; 229 SLIST_FOREACH(info, &mechs, link) { 230 *pp++ = info->name; 231 } 232 *pp = NULL; 233 } 234 235 static void 236 _rpc_gss_load_qop(void) 237 { 238 FILE *fp; 239 char buf[256]; 240 char *p; 241 char *name, *num, *mech; 242 struct mech_info *minfo; 243 struct qop_info *info; 244 int count; 245 const char **mech_qops; 246 const char **pp; 247 248 if (SLIST_FIRST(&qops)) 249 return; 250 251 fp = fopen(_PATH_GSS_QOP, "r"); 252 if (!fp) 253 return; 254 255 while (fgets(buf, sizeof(buf), fp)) { 256 if (*buf == '#') 257 continue; 258 p = buf; 259 name = strsep(&p, "\t\n "); 260 if (p) while (isspace(*p)) p++; 261 num = strsep(&p, "\t\n "); 262 if (p) while (isspace(*p)) p++; 263 mech = strsep(&p, "\t\n "); 264 if (!name || !num || !mech) 265 continue; 266 267 info = malloc(sizeof(struct qop_info)); 268 if (!info) 269 break; 270 info->name = strdup(name); 271 info->qop = strtoul(name, 0, 0); 272 info->mech = strdup(mech); 273 SLIST_INSERT_HEAD(&qops, info, link); 274 } 275 fclose(fp); 276 277 /* 278 * Compile lists of qops for each mechanism. 279 */ 280 SLIST_FOREACH(minfo, &mechs, link) { 281 count = 0; 282 SLIST_FOREACH(info, &qops, link) { 283 if (strcmp(info->mech, minfo->name) == 0) 284 count++; 285 } 286 mech_qops = malloc((count + 1) * sizeof(char*)); 287 pp = mech_qops; 288 SLIST_FOREACH(info, &qops, link) { 289 if (strcmp(info->mech, minfo->name) == 0) 290 *pp++ = info->name; 291 } 292 *pp = NULL; 293 minfo->qops = mech_qops; 294 } 295 } 296 297 bool_t 298 rpc_gss_mech_to_oid(const char *mech, gss_OID *oid_ret) 299 { 300 struct mech_info *info; 301 302 _rpc_gss_load_mech(); 303 SLIST_FOREACH(info, &mechs, link) { 304 if (!strcmp(info->name, mech)) { 305 *oid_ret = &info->oid; 306 return (TRUE); 307 } 308 } 309 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 310 return (FALSE); 311 } 312 313 bool_t 314 rpc_gss_oid_to_mech(gss_OID oid, const char **mech_ret) 315 { 316 struct mech_info *info; 317 318 _rpc_gss_load_mech(); 319 SLIST_FOREACH(info, &mechs, link) { 320 if (oid->length == info->oid.length 321 && !memcmp(oid->elements, info->oid.elements, 322 oid->length)) { 323 *mech_ret = info->name; 324 return (TRUE); 325 } 326 } 327 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 328 return (FALSE); 329 } 330 331 bool_t 332 rpc_gss_qop_to_num(const char *qop, const char *mech, u_int *num_ret) 333 { 334 struct qop_info *info; 335 336 _rpc_gss_load_qop(); 337 SLIST_FOREACH(info, &qops, link) { 338 if (strcmp(info->name, qop) == 0 339 && strcmp(info->mech, mech) == 0) { 340 *num_ret = info->qop; 341 return (TRUE); 342 } 343 } 344 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 345 return (FALSE); 346 } 347 348 const char * 349 _rpc_gss_num_to_qop(const char *mech, u_int num) 350 { 351 struct qop_info *info; 352 353 if (num == GSS_C_QOP_DEFAULT) 354 return "default"; 355 356 _rpc_gss_load_qop(); 357 SLIST_FOREACH(info, &qops, link) { 358 if (info->qop == num && strcmp(info->mech, mech) == 0) { 359 return (info->name); 360 } 361 } 362 return (NULL); 363 } 364 365 const char ** 366 rpc_gss_get_mechanisms(void) 367 { 368 369 _rpc_gss_load_mech(); 370 return (mech_names); 371 } 372 373 const char ** 374 rpc_gss_get_mech_info(const char *mech, rpc_gss_service_t *service) 375 { 376 struct mech_info *info; 377 378 _rpc_gss_load_mech(); 379 _rpc_gss_load_qop(); 380 SLIST_FOREACH(info, &mechs, link) { 381 if (!strcmp(mech, info->name)) { 382 /* 383 * I'm not sure what to do with service 384 * here. The Solaris manpages are not clear on 385 * the subject and the OpenSolaris code just 386 * sets it to rpc_gss_svc_privacy 387 * unconditionally with a comment noting that 388 * it is bogus. 389 */ 390 *service = rpc_gss_svc_privacy; 391 return info->qops; 392 } 393 } 394 395 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOENT); 396 return (NULL); 397 } 398 399 bool_t 400 rpc_gss_get_versions(u_int *vers_hi, u_int *vers_lo) 401 { 402 403 *vers_hi = 1; 404 *vers_lo = 1; 405 return (TRUE); 406 } 407 408 bool_t 409 rpc_gss_is_installed(const char *mech) 410 { 411 struct mech_info *info; 412 413 _rpc_gss_load_mech(); 414 SLIST_FOREACH(info, &mechs, link) 415 if (!strcmp(mech, info->name)) 416 return (TRUE); 417 return (FALSE); 418 } 419 420