xref: /onnv-gate/usr/src/cmd/gss/gsscred/gsscred_file.c (revision 1965:74cc7ce666a3)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*1965Ssemery  * Common Development and Distribution License (the "License").
6*1965Ssemery  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*1965Ssemery  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <stdio.h>
290Sstevel@tonic-gate #include <stdlib.h>
300Sstevel@tonic-gate #include <string.h>
310Sstevel@tonic-gate #include <unistd.h>
320Sstevel@tonic-gate #include <ctype.h>
330Sstevel@tonic-gate #include "gsscred.h"
340Sstevel@tonic-gate 
350Sstevel@tonic-gate /*
360Sstevel@tonic-gate  *  gsscred utility
370Sstevel@tonic-gate  *  Manages mapping between a security principal name and unix uid.
380Sstevel@tonic-gate  *  Implementation file for the file based gsscred utility.
390Sstevel@tonic-gate  */
400Sstevel@tonic-gate 
410Sstevel@tonic-gate #define	MAX_ENTRY_LEN 1024
42*1965Ssemery 
430Sstevel@tonic-gate static const char credFile[] = "/etc/gss/gsscred_db";
440Sstevel@tonic-gate static const char credFileTmp[] = "/etc/gss/gsscred_db.tmp";
45114Sdh145677 static const int expNameTokIdLen = 2;
46114Sdh145677 static const int mechOidLenLen = 2;
47*1965Ssemery static const int krb5OidTagLen = 1;
48*1965Ssemery static const int krb5OidLenLen = 1;
49*1965Ssemery static const int nameLen = 4;
50*1965Ssemery static const int krb5OidLen = 9;
51*1965Ssemery 
52*1965Ssemery /*
53*1965Ssemery  * Multiply by two given that the token has already gone through hex string
54*1965Ssemery  * expansion.
55*1965Ssemery  */
56*1965Ssemery #define	NAME_OFFSET (expNameTokIdLen + mechOidLenLen + krb5OidTagLen + \
57*1965Ssemery 	krb5OidLenLen + krb5OidLen + nameLen) * 2
580Sstevel@tonic-gate 
590Sstevel@tonic-gate static int matchEntry(const char *entry, const gss_buffer_t name,
600Sstevel@tonic-gate 		const char *uid, uid_t *uidOut);
610Sstevel@tonic-gate 
620Sstevel@tonic-gate /*
630Sstevel@tonic-gate  * file_addGssCredEntry
640Sstevel@tonic-gate  *
650Sstevel@tonic-gate  * Adds a new entry to the gsscred table.
660Sstevel@tonic-gate  * Does not check for duplicate entries.
670Sstevel@tonic-gate  */
file_addGssCredEntry(const gss_buffer_t hexName,const char * uid,const char * comment,char ** errDetails)680Sstevel@tonic-gate int file_addGssCredEntry(const gss_buffer_t hexName, const char *uid,
690Sstevel@tonic-gate 		const char *comment, char **errDetails)
700Sstevel@tonic-gate {
710Sstevel@tonic-gate 	FILE *fp;
720Sstevel@tonic-gate 	char tmpBuf[256];
730Sstevel@tonic-gate 
740Sstevel@tonic-gate 	if ((fp = fopen(credFile, "a")) == NULL) {
750Sstevel@tonic-gate 		if (errDetails) {
760Sstevel@tonic-gate 			(void) snprintf(tmpBuf, sizeof (tmpBuf),
770Sstevel@tonic-gate 				gettext("Unable to open gsscred file [%s]"),
780Sstevel@tonic-gate 				credFile);
790Sstevel@tonic-gate 			*errDetails = strdup(tmpBuf);
800Sstevel@tonic-gate 		}
810Sstevel@tonic-gate 		return (0);
820Sstevel@tonic-gate 	}
830Sstevel@tonic-gate 
840Sstevel@tonic-gate 	(void) fprintf(fp,
850Sstevel@tonic-gate 		    "%s\t%s\t%s\n", (char *)hexName->value, uid, comment);
860Sstevel@tonic-gate 	(void) fclose(fp);
870Sstevel@tonic-gate 	return (1);
880Sstevel@tonic-gate }  /* *******  file_addGssCredEntry ****** */
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 
920Sstevel@tonic-gate /*
930Sstevel@tonic-gate  * file_getGssCredEntry
940Sstevel@tonic-gate  *
950Sstevel@tonic-gate  * Searches the file for the file matching the name.  Since the name
960Sstevel@tonic-gate  * contains a mechanism identifier, to search for all names for a given
970Sstevel@tonic-gate  * mechanism just supply the mechanism portion in the name buffer.
980Sstevel@tonic-gate  * To search by uid only, supply a non-null value of uid.
990Sstevel@tonic-gate  */
file_getGssCredEntry(const gss_buffer_t name,const char * uid,char ** errDetails)1000Sstevel@tonic-gate int file_getGssCredEntry(const gss_buffer_t name, const char *uid,
1010Sstevel@tonic-gate 		char **errDetails)
1020Sstevel@tonic-gate {
1030Sstevel@tonic-gate 	FILE *fp;
1040Sstevel@tonic-gate 	char entry[MAX_ENTRY_LEN+1];
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate 	if ((fp = fopen(credFile, "r")) == NULL) {
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate 		if (errDetails) {
1090Sstevel@tonic-gate 			(void) snprintf(entry, sizeof (entry),
1100Sstevel@tonic-gate 				gettext("Unable to open gsscred file [%s]"),
1110Sstevel@tonic-gate 				credFile);
1120Sstevel@tonic-gate 			*errDetails = strdup(entry);
1130Sstevel@tonic-gate 		}
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 		return (0);
1160Sstevel@tonic-gate 	}
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate 	/* go through the file in sequential order */
1190Sstevel@tonic-gate 	while (fgets(entry, MAX_ENTRY_LEN, fp) != NULL) {
1200Sstevel@tonic-gate 		/* is there any search criteria */
1210Sstevel@tonic-gate 		if (name == NULL && uid == NULL) {
1220Sstevel@tonic-gate 			(void) fprintf(stdout, "%s", entry);
1230Sstevel@tonic-gate 			continue;
1240Sstevel@tonic-gate 		}
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 		if (matchEntry(entry, name, uid, NULL))
1270Sstevel@tonic-gate 			(void) fprintf(stdout, "%s", entry);
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 	}	 /* while */
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 	(void) fclose(fp);
1320Sstevel@tonic-gate 	return (1);
1330Sstevel@tonic-gate }  /* file_getGssCredEntry */
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate /*
1360Sstevel@tonic-gate  * file_getGssCredUid
1370Sstevel@tonic-gate  *
1380Sstevel@tonic-gate  * GSS entry point for retrieving user uid information.
1390Sstevel@tonic-gate  * We need to go through the entire file to ensure that
1400Sstevel@tonic-gate  * the last matching entry is retrieved - this is because
1410Sstevel@tonic-gate  * new entries are added to the end, and in case of
1420Sstevel@tonic-gate  * duplicates we want to get the latest entry.
1430Sstevel@tonic-gate  */
1440Sstevel@tonic-gate int
file_getGssCredUid(const gss_buffer_t expName,uid_t * uidOut)1450Sstevel@tonic-gate file_getGssCredUid(const gss_buffer_t expName, uid_t *uidOut)
1460Sstevel@tonic-gate {
1470Sstevel@tonic-gate 	FILE *fp;
1480Sstevel@tonic-gate 	char entry[MAX_ENTRY_LEN+1];
1490Sstevel@tonic-gate 	int retVal = 0;
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	if ((fp = fopen(credFile, "r")) == NULL)
1520Sstevel@tonic-gate 		return (0);
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 	/* go through the entire file in sequential order */
1550Sstevel@tonic-gate 	while (fgets(entry, MAX_ENTRY_LEN, fp) != NULL) {
1560Sstevel@tonic-gate 		if (matchEntry(entry, expName, NULL, uidOut)) {
1570Sstevel@tonic-gate 			retVal = 1;
1580Sstevel@tonic-gate 		}
1590Sstevel@tonic-gate 	} /* while */
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 	(void) fclose(fp);
1620Sstevel@tonic-gate 	return (retVal);
1630Sstevel@tonic-gate } /* file_getGssCredUid */
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate /*
1680Sstevel@tonic-gate  *
1690Sstevel@tonic-gate  * file_deleteGssCredEntry
1700Sstevel@tonic-gate  *
1710Sstevel@tonic-gate  * removes entries form file that match the delete criteria
1720Sstevel@tonic-gate  */
file_deleteGssCredEntry(const gss_buffer_t name,const char * uid,char ** errDetails)1730Sstevel@tonic-gate int file_deleteGssCredEntry(const gss_buffer_t name, const char *uid,
1740Sstevel@tonic-gate 		char **errDetails)
1750Sstevel@tonic-gate {
1760Sstevel@tonic-gate 	FILE *fp, *tempFp;
1770Sstevel@tonic-gate 	char entry[MAX_ENTRY_LEN+1];
1780Sstevel@tonic-gate 	int foundOne = 0;
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	/* are we deleting everyone? */
1810Sstevel@tonic-gate 	if (name == NULL && uid == NULL) {
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 		if ((fp = fopen(credFile, "w")) == NULL) {
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 			if (errDetails) {
1860Sstevel@tonic-gate 				(void) snprintf(entry, sizeof (entry),
1870Sstevel@tonic-gate 					gettext("Unable to open gsscred"
1880Sstevel@tonic-gate 						" file [%s]"),
1890Sstevel@tonic-gate 					credFile);
1900Sstevel@tonic-gate 				*errDetails = strdup(entry);
1910Sstevel@tonic-gate 			}
1920Sstevel@tonic-gate 			return (0);
1930Sstevel@tonic-gate 		}
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 		(void) fclose(fp);
1960Sstevel@tonic-gate 		return (1);
1970Sstevel@tonic-gate 	}
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	/* selective delete - might still be everyone */
2000Sstevel@tonic-gate 	if ((fp = fopen(credFile, "r")) == NULL) {
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 		if (errDetails) {
2030Sstevel@tonic-gate 			(void) snprintf(entry, sizeof (entry),
2040Sstevel@tonic-gate 				gettext("Unable to open gsscred file [%s]"),
2050Sstevel@tonic-gate 				credFile);
2060Sstevel@tonic-gate 			*errDetails = strdup(entry);
2070Sstevel@tonic-gate 		}
2080Sstevel@tonic-gate 		return (0);
2090Sstevel@tonic-gate 	}
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	/* also need to open temp file */
2120Sstevel@tonic-gate 	if ((tempFp = fopen(credFileTmp, "w")) == NULL) {
2130Sstevel@tonic-gate 		if (errDetails) {
2140Sstevel@tonic-gate 			(void) snprintf(entry, sizeof (entry),
2150Sstevel@tonic-gate 				gettext("Unable to open gsscred temporary"
2160Sstevel@tonic-gate 					" file [%s]"),
2170Sstevel@tonic-gate 				credFileTmp);
2180Sstevel@tonic-gate 			*errDetails = strdup(entry);
2190Sstevel@tonic-gate 		}
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 		(void) fclose(fp);
2220Sstevel@tonic-gate 		return (0);
2230Sstevel@tonic-gate 	}
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	/* go through all the entries sequentially removing ones that match */
2260Sstevel@tonic-gate 	while (fgets(entry, MAX_ENTRY_LEN, fp) != NULL) {
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 		if (!matchEntry(entry, name, uid, NULL))
2290Sstevel@tonic-gate 			(void) fputs(entry, tempFp);
2300Sstevel@tonic-gate 		else
2310Sstevel@tonic-gate 			foundOne = 1;
2320Sstevel@tonic-gate 	}
2330Sstevel@tonic-gate 	(void) fclose(tempFp);
2340Sstevel@tonic-gate 	(void) fclose(fp);
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 	/* now make the tempfile the gsscred file */
2370Sstevel@tonic-gate 	(void) rename(credFileTmp, credFile);
2380Sstevel@tonic-gate 	(void) unlink(credFileTmp);
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	if (!foundOne) {
2410Sstevel@tonic-gate 		*errDetails = strdup(gettext("No users found"));
2420Sstevel@tonic-gate 		return (0);
2430Sstevel@tonic-gate 	}
2440Sstevel@tonic-gate 	return (1);
2450Sstevel@tonic-gate }  /* file_deleteGssCredEntry */
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate /*
2500Sstevel@tonic-gate  *
2510Sstevel@tonic-gate  * match entry
2520Sstevel@tonic-gate  *
2530Sstevel@tonic-gate  * checks if the specified entry matches the supplied criteria
2540Sstevel@tonic-gate  * returns 1 if yes, 0 if no
2550Sstevel@tonic-gate  * uidOut value can be used to retrieve the uid from the entry
2560Sstevel@tonic-gate  * when the uid string is passed in, the uidOut value is not set
2570Sstevel@tonic-gate  */
matchEntry(const char * entry,const gss_buffer_t name,const char * uid,uid_t * uidOut)2580Sstevel@tonic-gate static int matchEntry(const char *entry, const gss_buffer_t name,
2590Sstevel@tonic-gate 		const char *uid, uid_t *uidOut)
2600Sstevel@tonic-gate {
261*1965Ssemery 	char fullEntry[MAX_ENTRY_LEN+1], *item, *item_buf, *name_buf;
2620Sstevel@tonic-gate 	char dilims[] = "\t \n";
263*1965Ssemery 	/*
264*1965Ssemery 	 * item_len is the length of the token in the gsscred_db.
265*1965Ssemery 	 * name_len is the length of the token passed to this function.
266*1965Ssemery 	 */
267*1965Ssemery 	int item_len, name_len;
268*1965Ssemery 	/*
269*1965Ssemery 	 * This is the hex encoding of the beginning of all exported name
270*1965Ssemery 	 * tokens for the Kerberos V mechanism.  We need this to detect old,
271*1965Ssemery 	 * incorrectly exported name tokens; see below.
272*1965Ssemery 	 */
273*1965Ssemery 	char *krb5_ntok_prefix = "0401000B06092A864886F712010202";
274*1965Ssemery 	/*
275*1965Ssemery 	 * This is the hex encoded GSS_C_NT_USER_NAME OID, needed for the same
276*1965Ssemery 	 * reason as krb5_ntok_prefix.
277*1965Ssemery 	 */
278*1965Ssemery 	char *gss_u_name = "2A864886F71201020101";
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	if (entry == NULL || isspace(*entry))
2810Sstevel@tonic-gate 		return (0);
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	/* save the entry since strtok will chop it up */
2840Sstevel@tonic-gate 	(void) strcpy(fullEntry, entry);
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	if ((item = strtok(fullEntry, dilims)) == NULL)
2870Sstevel@tonic-gate 		return (0);
2880Sstevel@tonic-gate 
289*1965Ssemery 	/* do we need to search the name */
2900Sstevel@tonic-gate 	if (name != NULL) {
291*1965Ssemery 
292*1965Ssemery 		item_len = strlen(item);
293*1965Ssemery 		name_len = name->length;
294*1965Ssemery 		name_buf = name->value;
295*1965Ssemery 
2960Sstevel@tonic-gate 		/* we can match the prefix of the string */
297*1965Ssemery 		if (item_len < name_len)
2980Sstevel@tonic-gate 			return (0);
2990Sstevel@tonic-gate 
300*1965Ssemery 		if (strncmp(item, name->value, name_len) != 0) {
301114Sdh145677 
302114Sdh145677 			/*
303*1965Ssemery 			 * The following section is needed in order to detect
304*1965Ssemery 			 * two existing errant formats in the gsscred db.
305*1965Ssemery 			 *
306*1965Ssemery 			 * 1. Exported names that have a trailing null byte
307*1965Ssemery 			 * ("00" in two hex characters) with the name length
308*1965Ssemery 			 * incremented to account for the extra null byte.
309*1965Ssemery 			 *
310*1965Ssemery 			 * 2. Exported names that have the name type length
311*1965Ssemery 			 * and name type OID prepended to the exported name.
312*1965Ssemery 			 *
313114Sdh145677 			 */
314*1965Ssemery 			if (strncmp(name->value, krb5_ntok_prefix,
315*1965Ssemery 			    strlen(krb5_ntok_prefix)) != 0)
316114Sdh145677 				return (0);
317114Sdh145677 
318*1965Ssemery 			if (strncmp(item, krb5_ntok_prefix,
319*1965Ssemery 			    strlen(krb5_ntok_prefix)) != 0)
320*1965Ssemery 				return (0);
321*1965Ssemery 
322*1965Ssemery 			if ((item_buf = strstr(item, gss_u_name)) == NULL)
323*1965Ssemery 				return (0);
324114Sdh145677 
325*1965Ssemery 			item_buf += strlen(gss_u_name);
326*1965Ssemery 
327*1965Ssemery 			name_buf += NAME_OFFSET;
328*1965Ssemery 
329*1965Ssemery 			if ((strlen(item_buf) != strlen(name_buf)) &&
330*1965Ssemery 			    (strncmp(item_buf + (strlen(item_buf) - 2), "00", 2)
331*1965Ssemery 			    != 0))
332*1965Ssemery 				return (0);
333114Sdh145677 
334114Sdh145677 			/*
335*1965Ssemery 			 * Here we compare the end of name_len, given
336*1965Ssemery 			 * that item_len could have two extra "00"
337*1965Ssemery 			 * representing the null byte.
338114Sdh145677 			 */
339*1965Ssemery 			if (strncmp(item_buf, name_buf, name_len - NAME_OFFSET)
340*1965Ssemery 			    != 0)
341*1965Ssemery 				return (0);
342*1965Ssemery 		} else
343*1965Ssemery 			/*
344*1965Ssemery 			 * We only strncmp() so we could check for old,
345*1965Ssemery 			 * broken exported name tokens for the krb5 mech.
346*1965Ssemery 			 * For any other exported name tokens we want exact
347*1965Ssemery 			 * matches only.
348*1965Ssemery 			 */
349*1965Ssemery 			if (item_len != name_len)
350*1965Ssemery 				return (0);
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 		/* do we need to check the uid - if not then we found it */
3530Sstevel@tonic-gate 		if (uid == NULL) {
3540Sstevel@tonic-gate 			/* do we ned to parse out the uid ? */
3550Sstevel@tonic-gate 			if (uidOut) {
3560Sstevel@tonic-gate 				if ((item = strtok(NULL, dilims)) == NULL)
3570Sstevel@tonic-gate 					return (0);
3580Sstevel@tonic-gate 				*uidOut = atol(item);
3590Sstevel@tonic-gate 			}
3600Sstevel@tonic-gate 			return (1);
3610Sstevel@tonic-gate 		}
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 		/* continue with checking the uid */
3640Sstevel@tonic-gate 	}
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 	if (uid == NULL)
3670Sstevel@tonic-gate 		return (1);
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate 	/* get the next token from the string - the uid */
3700Sstevel@tonic-gate 	if ((item = strtok(NULL, dilims)) == NULL)
3710Sstevel@tonic-gate 		return (0);
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	if (strcmp(item, uid) == 0)
3740Sstevel@tonic-gate 		return (1);
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	return (0);
3770Sstevel@tonic-gate }  /* *******  matchEntry ****** */
378