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
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
_rpc_gss_string_to_oid(const char * s,gss_OID oid)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
_rpc_gss_load_mech(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
_rpc_gss_load_qop(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
rpc_gss_mech_to_oid(const char * mech,gss_OID * oid_ret)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
rpc_gss_oid_to_mech(gss_OID oid,const char ** mech_ret)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
rpc_gss_qop_to_num(const char * qop,const char * mech,u_int * num_ret)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 *
_rpc_gss_num_to_qop(const char * mech,u_int num)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 **
rpc_gss_get_mechanisms(void)364 rpc_gss_get_mechanisms(void)
365 {
366
367 _rpc_gss_load_mech();
368 return (mech_names);
369 }
370
371 const char **
rpc_gss_get_mech_info(const char * mech,rpc_gss_service_t * service)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
rpc_gss_get_versions(u_int * vers_hi,u_int * vers_lo)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
rpc_gss_is_installed(const char * mech)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