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 *
create_mech(char * name)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
free_mechlist(mechlist_t * plist)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
is_in_list(char * mechname,mechlist_t * plist)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
update_conf(char * conf_file,char * entry)109 update_conf(char *conf_file, char *entry)
110 {
111 
112 	boolean_t	found;
113 	boolean_t	fips_entry = B_FALSE;
114 	FILE	*pfile;
115 	FILE	*pfile_tmp;
116 	char	tmpfile_name[MAXPATHLEN];
117 	char	*ptr;
118 	char	*name;
119 	char	buffer[BUFSIZ];
120 	char	buffer2[BUFSIZ];
121 	int		found_count;
122 	int		rc = SUCCESS;
123 	int		err;
124 
125 	if ((pfile = fopen(conf_file, "r+")) == NULL) {
126 		err = errno;
127 		cryptoerror(LOG_STDERR,
128 		    gettext("failed to update the configuration - %s"),
129 		    strerror(err));
130 		cryptodebug("failed to open %s for write.", conf_file);
131 		return (FAILURE);
132 	}
133 
134 	if (lockf(fileno(pfile), F_TLOCK, 0) == -1) {
135 		err = errno;
136 		cryptoerror(LOG_STDERR,
137 		    gettext("failed to lock the configuration - %s"),
138 		    strerror(err));
139 		(void) fclose(pfile);
140 		return (FAILURE);
141 	}
142 
143 	/*
144 	 * Create a temporary file in the /etc/crypto directory.
145 	 */
146 	(void) strlcpy(tmpfile_name, TMPFILE_TEMPLATE, sizeof (tmpfile_name));
147 	if (mkstemp(tmpfile_name) == -1) {
148 		err = errno;
149 		cryptoerror(LOG_STDERR,
150 		    gettext("failed to create a temporary file - %s"),
151 		    strerror(err));
152 		(void) fclose(pfile);
153 		return (FAILURE);
154 	}
155 
156 	if ((pfile_tmp = fopen(tmpfile_name, "w")) == NULL) {
157 		err = errno;
158 		cryptoerror(LOG_STDERR, gettext("failed to open %s - %s"),
159 		    tmpfile_name, strerror(err));
160 		(void) fclose(pfile);
161 		return (FAILURE);
162 	}
163 
164 
165 	/*
166 	 * Loop thru the config file. If the provider was reserved within a
167 	 * package bracket, just uncomment it.  Otherwise, append it at
168 	 * the end.  The resulting file will be saved in the temp file first.
169 	 */
170 	found_count = 0;
171 	rc = SUCCESS;
172 
173 	while (fgets(buffer, BUFSIZ, pfile) != NULL) {
174 		found = B_FALSE;
175 		if (strcmp(conf_file, _PATH_PKCS11_CONF) == 0) {
176 			if (buffer[0] == '#') {
177 				ptr = buffer;
178 				ptr++;
179 				if (strcmp(entry, ptr) == 0) {
180 					found = B_TRUE;
181 					found_count++;
182 				}
183 			} else {
184 				(void) strlcpy(buffer2, buffer, BUFSIZ);
185 				ptr = buffer2;
186 				if ((name = strtok(ptr, SEP_COLON)) == NULL) {
187 					rc = FAILURE;
188 					break;
189 				} else if (strcmp(FIPS_KEYWORD, name) == 0) {
190 					found = B_TRUE;
191 					found_count++;
192 					fips_entry = B_TRUE;
193 				}
194 			}
195 		} else { /* _PATH_KCF_CONF */
196 			if (buffer[0] == '#') {
197 				(void) strlcpy(buffer2, buffer, BUFSIZ);
198 				ptr = buffer2;
199 				ptr++; /* skip # */
200 				if ((name = strtok(ptr, SEP_COLON)) == NULL) {
201 					rc = FAILURE;
202 					break;
203 				}
204 			} else {
205 				(void) strlcpy(buffer2, buffer, BUFSIZ);
206 				ptr = buffer2;
207 				if ((name = strtok(ptr, SEP_COLON)) == NULL) {
208 					rc = FAILURE;
209 					break;
210 				}
211 			}
212 		}
213 
214 		if (found == B_FALSE) {
215 			if (fputs(buffer, pfile_tmp) == EOF) {
216 				rc = FAILURE;
217 			}
218 		} else {
219 			if (found_count == 1) {
220 				if (strcmp(conf_file, _PATH_PKCS11_CONF) == 0) {
221 					if (fips_entry == B_TRUE) {
222 						if (fputs(entry, pfile_tmp) ==
223 						    EOF) {
224 							rc = FAILURE;
225 						}
226 						fips_entry = B_FALSE;
227 					} else {
228 						if (fputs(ptr, pfile_tmp) ==
229 						    EOF) {
230 							rc = FAILURE;
231 						}
232 					}
233 				} else {
234 					if (fputs(entry, pfile_tmp) == EOF) {
235 						rc = FAILURE;
236 					}
237 				}
238 			} else {
239 				/*
240 				 * Found a second entry with same tag name.
241 				 * Should not happen. The config file
242 				 * is corrupted. Give a warning and skip
243 				 * this entry.
244 				 */
245 				cryptoerror(LOG_STDERR, gettext(
246 				    "(Warning) Found an additional reserved "
247 				    "entry for %s."), entry);
248 			}
249 		}
250 
251 		if (rc == FAILURE) {
252 			break;
253 		}
254 	}
255 
256 	(void) fclose(pfile);
257 
258 	if (rc == FAILURE) {
259 		cryptoerror(LOG_STDERR, gettext("write error."));
260 		(void) fclose(pfile_tmp);
261 		if (unlink(tmpfile_name) != 0) {
262 			err = errno;
263 			cryptoerror(LOG_STDERR, gettext(
264 			    "(Warning) failed to remove %s: %s"), tmpfile_name,
265 			    strerror(err));
266 		}
267 		return (FAILURE);
268 	}
269 
270 	if (found_count == 0) {
271 		/*
272 		 * The entry was not in config file before, append it to the
273 		 * end of the temp file.
274 		 */
275 		if (fputs(entry, pfile_tmp) == EOF) {
276 			cryptoerror(LOG_STDERR, gettext(
277 			    "failed to write to %s: %s"), tmpfile_name,
278 			    strerror(errno));
279 			(void) fclose(pfile_tmp);
280 			if (unlink(tmpfile_name) != 0) {
281 				err = errno;
282 				cryptoerror(LOG_STDERR, gettext(
283 				    "(Warning) failed to remove %s: %s"),
284 				    tmpfile_name, strerror(err));
285 			}
286 			return (FAILURE);
287 		}
288 	}
289 
290 	if (fclose(pfile_tmp) != 0) {
291 		err = errno;
292 		cryptoerror(LOG_STDERR,
293 		    gettext("failed to close %s: %s"), tmpfile_name,
294 		    strerror(err));
295 		return (FAILURE);
296 	}
297 
298 	if (rename(tmpfile_name, conf_file) == -1) {
299 		err = errno;
300 		cryptoerror(LOG_STDERR,
301 		    gettext("failed to update the configuration - %s"),
302 		    strerror(err));
303 		cryptodebug("failed to rename %s to %s: %s", tmpfile_name,
304 		    conf_file, strerror(err));
305 		rc = FAILURE;
306 	} else if (chmod(conf_file,
307 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
308 		err = errno;
309 		cryptoerror(LOG_STDERR,
310 		    gettext("failed to update the configuration - %s"),
311 		    strerror(err));
312 		cryptodebug("failed to chmod to %s: %s", conf_file,
313 		    strerror(err));
314 		rc = FAILURE;
315 	} else {
316 		rc = SUCCESS;
317 	}
318 
319 	if (rc == FAILURE) {
320 		if (unlink(tmpfile_name) != 0) {
321 			err = errno;
322 			cryptoerror(LOG_STDERR, gettext(
323 			    "(Warning) failed to remove %s: %s"),
324 			    tmpfile_name, strerror(err));
325 		}
326 	}
327 
328 	return (rc);
329 
330 }
331