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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <ctype.h>
27 #include <strings.h>
28 #include <libintl.h>
29 #include <stdio.h>
30 #include <sys/stat.h>
31 #include "cryptoadm.h"
32 #include <cryptoutil.h>
33 
34 /*
35  * Create one item of type mechlist_t with the mechanism name.  A null is
36  * returned to indicate that the storage space available is insufficient.
37  */
38 mechlist_t *
39 create_mech(char *name)
40 {
41 	mechlist_t *pres = NULL;
42 	char *first, *last;
43 
44 	if (name == NULL) {
45 		return (NULL);
46 	}
47 
48 	pres = malloc(sizeof (mechlist_t));
49 	if (pres == NULL) {
50 		cryptodebug("out of memory.");
51 		return (NULL);
52 	}
53 
54 	first = name;
55 	while (isspace(*first)) /* nuke leading whitespace */
56 		first++;
57 	(void) strlcpy(pres->name, first, sizeof (pres->name));
58 
59 	last = strrchr(pres->name, '\0');
60 	last--;
61 	while (isspace(*last))  /* nuke trailing whitespace */
62 		*last-- = '\0';
63 
64 	pres->next = NULL;
65 
66 	return (pres);
67 }
68 
69 
70 
71 void
72 free_mechlist(mechlist_t *plist)
73 {
74 	mechlist_t *pnext;
75 
76 	while (plist != NULL) {
77 		pnext = plist->next;
78 		free(plist);
79 		plist = pnext;
80 	}
81 }
82 
83 
84 
85 /*
86  * Check if the mechanism is in the mechanism list.
87  */
88 boolean_t
89 is_in_list(char *mechname, mechlist_t *plist)
90 {
91 	boolean_t found = B_FALSE;
92 
93 	if (mechname == NULL) {
94 		return (B_FALSE);
95 	}
96 
97 	while (plist != NULL) {
98 		if (strcmp(plist->name, mechname) == 0) {
99 			found = B_TRUE;
100 			break;
101 		}
102 		plist = plist->next;
103 	}
104 
105 	return (found);
106 }
107 
108 int
109 update_conf(char *conf_file, char *entry)
110 {
111 
112 	boolean_t	found;
113 	FILE	*pfile;
114 	FILE	*pfile_tmp;
115 	char	tmpfile_name[MAXPATHLEN];
116 	char	*ptr;
117 	char	*name;
118 	char	buffer[BUFSIZ];
119 	char	buffer2[BUFSIZ];
120 	int		found_count;
121 	int		rc = SUCCESS;
122 	int		err;
123 
124 	if ((pfile = fopen(conf_file, "r+")) == NULL) {
125 		err = errno;
126 		cryptoerror(LOG_STDERR,
127 		    gettext("failed to update the configuration - %s"),
128 		    strerror(err));
129 		cryptodebug("failed to open %s for write.", conf_file);
130 		return (FAILURE);
131 	}
132 
133 	if (lockf(fileno(pfile), F_TLOCK, 0) == -1) {
134 		err = errno;
135 		cryptoerror(LOG_STDERR,
136 		    gettext("failed to lock the configuration - %s"),
137 		    strerror(err));
138 		(void) fclose(pfile);
139 		return (FAILURE);
140 	}
141 
142 	/*
143 	 * Create a temporary file in the /etc/crypto directory.
144 	 */
145 	(void) strlcpy(tmpfile_name, TMPFILE_TEMPLATE, sizeof (tmpfile_name));
146 	if (mkstemp(tmpfile_name) == -1) {
147 		err = errno;
148 		cryptoerror(LOG_STDERR,
149 		    gettext("failed to create a temporary file - %s"),
150 		    strerror(err));
151 		(void) fclose(pfile);
152 		return (FAILURE);
153 	}
154 
155 	if ((pfile_tmp = fopen(tmpfile_name, "w")) == NULL) {
156 		err = errno;
157 		cryptoerror(LOG_STDERR, gettext("failed to open %s - %s"),
158 		    tmpfile_name, strerror(err));
159 		(void) fclose(pfile);
160 		return (FAILURE);
161 	}
162 
163 
164 	/*
165 	 * Loop thru the config file. If the provider was reserved within a
166 	 * package bracket, just uncomment it.  Otherwise, append it at
167 	 * the end.  The resulting file will be saved in the temp file first.
168 	 */
169 	found_count = 0;
170 	rc = SUCCESS;
171 
172 	while (fgets(buffer, BUFSIZ, pfile) != NULL) {
173 		found = B_FALSE;
174 		if (strcmp(conf_file, _PATH_PKCS11_CONF) == 0) {
175 			if (buffer[0] == '#') {
176 				ptr = buffer;
177 				ptr++;
178 				if (strcmp(entry, ptr) == 0) {
179 					found = B_TRUE;
180 					found_count++;
181 				}
182 			}
183 		} else { /* _PATH_KCF_CONF */
184 			if (buffer[0] == '#') {
185 				(void) strlcpy(buffer2, buffer, BUFSIZ);
186 				ptr = buffer2;
187 				ptr++; /* skip # */
188 				if ((name = strtok(ptr, SEP_COLON)) == NULL) {
189 					rc = FAILURE;
190 					break;
191 				} else if (strcmp(FIPS_KEYWORD, name) == 0) {
192 					found = B_TRUE;
193 					found_count++;
194 				}
195 			} else {
196 				(void) strlcpy(buffer2, buffer, BUFSIZ);
197 				ptr = buffer2;
198 				if ((name = strtok(ptr, SEP_COLON)) == NULL) {
199 					rc = FAILURE;
200 					break;
201 				} else if (strcmp(FIPS_KEYWORD, name) == 0) {
202 					found = B_TRUE;
203 					found_count++;
204 				}
205 			}
206 		}
207 
208 		if (found == B_FALSE) {
209 			if (fputs(buffer, pfile_tmp) == EOF) {
210 				rc = FAILURE;
211 			}
212 		} else {
213 			if (found_count == 1) {
214 				if (strcmp(conf_file, _PATH_PKCS11_CONF) == 0) {
215 					if (fputs(ptr, pfile_tmp) == EOF) {
216 						rc = FAILURE;
217 					}
218 				} else {
219 					if (fputs(entry, pfile_tmp) == EOF) {
220 						rc = FAILURE;
221 					}
222 				}
223 			} else {
224 				/*
225 				 * Found a second entry with same tag name.
226 				 * Should not happen. The config file
227 				 * is corrupted. Give a warning and skip
228 				 * this entry.
229 				 */
230 				cryptoerror(LOG_STDERR, gettext(
231 				    "(Warning) Found an additional reserved "
232 				    "entry for %s."), entry);
233 			}
234 		}
235 
236 		if (rc == FAILURE) {
237 			break;
238 		}
239 	}
240 
241 	(void) fclose(pfile);
242 
243 	if (rc == FAILURE) {
244 		cryptoerror(LOG_STDERR, gettext("write error."));
245 		(void) fclose(pfile_tmp);
246 		if (unlink(tmpfile_name) != 0) {
247 			err = errno;
248 			cryptoerror(LOG_STDERR, gettext(
249 			    "(Warning) failed to remove %s: %s"), tmpfile_name,
250 			    strerror(err));
251 		}
252 		return (FAILURE);
253 	}
254 
255 	if (found_count == 0) {
256 		/*
257 		 * The entry was not in config file before, append it to the
258 		 * end of the temp file.
259 		 */
260 		if (fputs(entry, pfile_tmp) == EOF) {
261 			cryptoerror(LOG_STDERR, gettext(
262 			    "failed to write to %s: %s"), tmpfile_name,
263 			    strerror(errno));
264 			(void) fclose(pfile_tmp);
265 			if (unlink(tmpfile_name) != 0) {
266 				err = errno;
267 				cryptoerror(LOG_STDERR, gettext(
268 				    "(Warning) failed to remove %s: %s"),
269 				    tmpfile_name, strerror(err));
270 			}
271 			return (FAILURE);
272 		}
273 	}
274 
275 	if (fclose(pfile_tmp) != 0) {
276 		err = errno;
277 		cryptoerror(LOG_STDERR,
278 		    gettext("failed to close %s: %s"), tmpfile_name,
279 		    strerror(err));
280 		return (FAILURE);
281 	}
282 
283 	if (rename(tmpfile_name, conf_file) == -1) {
284 		err = errno;
285 		cryptoerror(LOG_STDERR,
286 		    gettext("failed to update the configuration - %s"),
287 		    strerror(err));
288 		cryptodebug("failed to rename %s to %s: %s", tmpfile_name,
289 		    conf_file, strerror(err));
290 		rc = FAILURE;
291 	} else if (chmod(conf_file,
292 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
293 		err = errno;
294 		cryptoerror(LOG_STDERR,
295 		    gettext("failed to update the configuration - %s"),
296 		    strerror(err));
297 		cryptodebug("failed to chmod to %s: %s", conf_file,
298 		    strerror(err));
299 		rc = FAILURE;
300 	} else {
301 		rc = SUCCESS;
302 	}
303 
304 	if (rc == FAILURE) {
305 		if (unlink(tmpfile_name) != 0) {
306 			err = errno;
307 			cryptoerror(LOG_STDERR, gettext(
308 			    "(Warning) failed to remove %s: %s"),
309 			    tmpfile_name, strerror(err));
310 		}
311 	}
312 
313 	return (rc);
314 
315 }
316