1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include "gsscred.h"
35 
36 /*
37  *  gsscred utility
38  *  Manages mapping between a security principal name and unix uid.
39  *  Implementation file for the file based gsscred utility.
40  */
41 
42 #define	MAX_ENTRY_LEN 1024
43 static const char credFile[] = "/etc/gss/gsscred_db";
44 static const char credFileTmp[] = "/etc/gss/gsscred_db.tmp";
45 static const int expNameTokIdLen = 2;
46 static const int mechOidLenLen = 2;
47 static const int mechOidTagLen = 1;
48 
49 static int matchEntry(const char *entry, const gss_buffer_t name,
50 		const char *uid, uid_t *uidOut);
51 
52 /* From g_glue.c */
53 extern int
54 get_der_length(unsigned char **, unsigned int, unsigned int *);
55 
56 /*
57  * file_addGssCredEntry
58  *
59  * Adds a new entry to the gsscred table.
60  * Does not check for duplicate entries.
61  */
62 int file_addGssCredEntry(const gss_buffer_t hexName, const char *uid,
63 		const char *comment, char **errDetails)
64 {
65 	FILE *fp;
66 	char tmpBuf[256];
67 
68 	if ((fp = fopen(credFile, "a")) == NULL) {
69 		if (errDetails) {
70 			(void) snprintf(tmpBuf, sizeof (tmpBuf),
71 				gettext("Unable to open gsscred file [%s]"),
72 				credFile);
73 			*errDetails = strdup(tmpBuf);
74 		}
75 		return (0);
76 	}
77 
78 	(void) fprintf(fp,
79 		    "%s\t%s\t%s\n", (char *)hexName->value, uid, comment);
80 	(void) fclose(fp);
81 	return (1);
82 }  /* *******  file_addGssCredEntry ****** */
83 
84 
85 
86 /*
87  * file_getGssCredEntry
88  *
89  * Searches the file for the file matching the name.  Since the name
90  * contains a mechanism identifier, to search for all names for a given
91  * mechanism just supply the mechanism portion in the name buffer.
92  * To search by uid only, supply a non-null value of uid.
93  */
94 int file_getGssCredEntry(const gss_buffer_t name, const char *uid,
95 		char **errDetails)
96 {
97 	FILE *fp;
98 	char entry[MAX_ENTRY_LEN+1];
99 
100 	if ((fp = fopen(credFile, "r")) == NULL) {
101 
102 		if (errDetails) {
103 			(void) snprintf(entry, sizeof (entry),
104 				gettext("Unable to open gsscred file [%s]"),
105 				credFile);
106 			*errDetails = strdup(entry);
107 		}
108 
109 		return (0);
110 	}
111 
112 	/* go through the file in sequential order */
113 	while (fgets(entry, MAX_ENTRY_LEN, fp) != NULL) {
114 		/* is there any search criteria */
115 		if (name == NULL && uid == NULL) {
116 			(void) fprintf(stdout, "%s", entry);
117 			continue;
118 		}
119 
120 		if (matchEntry(entry, name, uid, NULL))
121 			(void) fprintf(stdout, "%s", entry);
122 
123 	}	 /* while */
124 
125 	(void) fclose(fp);
126 	return (1);
127 }  /* file_getGssCredEntry */
128 
129 /*
130  * file_getGssCredUid
131  *
132  * GSS entry point for retrieving user uid information.
133  * We need to go through the entire file to ensure that
134  * the last matching entry is retrieved - this is because
135  * new entries are added to the end, and in case of
136  * duplicates we want to get the latest entry.
137  */
138 int
139 file_getGssCredUid(const gss_buffer_t expName, uid_t *uidOut)
140 {
141 	FILE *fp;
142 	char entry[MAX_ENTRY_LEN+1];
143 	int retVal = 0;
144 
145 	if ((fp = fopen(credFile, "r")) == NULL)
146 		return (0);
147 
148 	/* go through the entire file in sequential order */
149 	while (fgets(entry, MAX_ENTRY_LEN, fp) != NULL) {
150 		if (matchEntry(entry, expName, NULL, uidOut)) {
151 			retVal = 1;
152 		}
153 	} /* while */
154 
155 	(void) fclose(fp);
156 	return (retVal);
157 } /* file_getGssCredUid */
158 
159 
160 
161 /*
162  *
163  * file_deleteGssCredEntry
164  *
165  * removes entries form file that match the delete criteria
166  */
167 int file_deleteGssCredEntry(const gss_buffer_t name, const char *uid,
168 		char **errDetails)
169 {
170 	FILE *fp, *tempFp;
171 	char entry[MAX_ENTRY_LEN+1];
172 	int foundOne = 0;
173 
174 	/* are we deleting everyone? */
175 	if (name == NULL && uid == NULL) {
176 
177 		if ((fp = fopen(credFile, "w")) == NULL) {
178 
179 			if (errDetails) {
180 				(void) snprintf(entry, sizeof (entry),
181 					gettext("Unable to open gsscred"
182 						" file [%s]"),
183 					credFile);
184 				*errDetails = strdup(entry);
185 			}
186 			return (0);
187 		}
188 
189 		(void) fclose(fp);
190 		return (1);
191 	}
192 
193 	/* selective delete - might still be everyone */
194 	if ((fp = fopen(credFile, "r")) == NULL) {
195 
196 		if (errDetails) {
197 			(void) snprintf(entry, sizeof (entry),
198 				gettext("Unable to open gsscred file [%s]"),
199 				credFile);
200 			*errDetails = strdup(entry);
201 		}
202 		return (0);
203 	}
204 
205 	/* also need to open temp file */
206 	if ((tempFp = fopen(credFileTmp, "w")) == NULL) {
207 		if (errDetails) {
208 			(void) snprintf(entry, sizeof (entry),
209 				gettext("Unable to open gsscred temporary"
210 					" file [%s]"),
211 				credFileTmp);
212 			*errDetails = strdup(entry);
213 		}
214 
215 		(void) fclose(fp);
216 		return (0);
217 	}
218 
219 	/* go through all the entries sequentially removing ones that match */
220 	while (fgets(entry, MAX_ENTRY_LEN, fp) != NULL) {
221 
222 		if (!matchEntry(entry, name, uid, NULL))
223 			(void) fputs(entry, tempFp);
224 		else
225 			foundOne = 1;
226 	}
227 	(void) fclose(tempFp);
228 	(void) fclose(fp);
229 
230 	/* now make the tempfile the gsscred file */
231 	(void) rename(credFileTmp, credFile);
232 	(void) unlink(credFileTmp);
233 
234 	if (!foundOne) {
235 		*errDetails = strdup(gettext("No users found"));
236 		return (0);
237 	}
238 	return (1);
239 }  /* file_deleteGssCredEntry */
240 
241 
242 
243 /*
244  *
245  * match entry
246  *
247  * checks if the specified entry matches the supplied criteria
248  * returns 1 if yes, 0 if no
249  * uidOut value can be used to retrieve the uid from the entry
250  * when the uid string is passed in, the uidOut value is not set
251  */
252 static int matchEntry(const char *entry, const gss_buffer_t name,
253 		const char *uid, uid_t *uidOut)
254 {
255 	char fullEntry[MAX_ENTRY_LEN+1], *item;
256 	unsigned char *buf;
257 	char dilims[] = "\t \n";
258 	int length;
259 	unsigned int dummy;
260 	OM_uint32 minor, result;
261 	gss_buffer_desc mechOidDesc = GSS_C_EMPTY_BUFFER;
262 	gss_name_t intName;
263 	gss_buffer_desc expName;
264 	char *krb5_oid = "\052\206\110\206\367\022\001\002\002";
265 
266 	if (entry == NULL || isspace(*entry))
267 		return (0);
268 
269 	/* save the entry since strtok will chop it up */
270 	(void) strcpy(fullEntry, entry);
271 
272 	if ((item = strtok(fullEntry, dilims)) == NULL)
273 		return (0);
274 
275 	/* do wee need to search the name */
276 	if (name != NULL) {
277 		/* we can match the prefix of the string */
278 		if (strlen(item) < name->length)
279 			return (0);
280 
281 		if (memcmp(item, name->value, name->length) != 0) {
282 
283 			buf = (unsigned char *)name->value;
284 			buf += expNameTokIdLen;
285 
286 			/* skip oid length - get to der */
287 			buf += 2;
288 
289 			/* skip oid tag */
290 			buf++;
291 
292 			/* get oid length */
293 			length = get_der_length(&buf,
294 				(name->length - expNameTokIdLen
295 				- mechOidLenLen - mechOidTagLen), &dummy);
296 
297 			if (length == -1)
298 				return (0);
299 
300 			mechOidDesc.length = length;
301 
302 			/*
303 			 * check whether exported name length is within the
304 			 * boundary.
305 			 */
306 			if (name->length <
307 				(expNameTokIdLen + mechOidLenLen + length
308 					+ dummy + mechOidTagLen))
309 				return (0);
310 
311 			mechOidDesc.value = buf;
312 
313 			buf += dummy + mechOidDesc.length;
314 
315 			/*
316 			 * If the mechoid is that of Kerberos and if the
317 			 * "display" part of the exported file name starts and
318 			 * ends with a zero-valued byte, then we are dealing
319 			 * with old styled gsscred entries. We will then match
320 			 * them in the following manner:
321 			 *	- gss_import_name() the name from the file
322 			 *	- gss_export_name() the result
323 			 *	- mem_cmp() the result with the name we are
324 			 *		trying to match.
325 			 */
326 			if (mechOidDesc.length == 9 &&
327 				(memcmp(buf, krb5_oid,
328 						mechOidDesc.length) == 0) &&
329 				(*buf == '\0' && buf[length] == '\0')) {
330 				if (gss_import_name(&minor, name,
331 						GSS_C_NT_EXPORT_NAME,
332 						&intName) != GSS_S_COMPLETE)
333 					return (0);
334 				result = gss_export_name(&minor, intName,
335 								&expName);
336 				(void) gss_release_name(&minor, &intName);
337 				if (result != GSS_S_COMPLETE)
338 					return (0);
339 				result = memcmp(item, expName.value,
340 							expName.length);
341 				(void) gss_release_buffer(&minor, &expName);
342 				if (result != 0)
343 					return (0);
344 			}
345 		}
346 
347 		/* do we need to check the uid - if not then we found it */
348 		if (uid == NULL) {
349 			/* do we ned to parse out the uid ? */
350 			if (uidOut) {
351 				if ((item = strtok(NULL, dilims)) == NULL)
352 					return (0);
353 				*uidOut = atol(item);
354 			}
355 			return (1);
356 		}
357 
358 		/* continue with checking the uid */
359 	}
360 
361 	if (uid == NULL)
362 		return (1);
363 
364 	/* get the next token from the string - the uid */
365 	if ((item = strtok(NULL, dilims)) == NULL)
366 		return (0);
367 
368 	if (strcmp(item, uid) == 0)
369 		return (1);
370 
371 	return (0);
372 }  /* *******  matchEntry ****** */
373