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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  */
24 
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 #include <stdio.h>
28 #include <strings.h>
29 #include <ctype.h>
30 #include <libgen.h>
31 #include <libintl.h>
32 #include <errno.h>
33 #include <kmfapiP.h>
34 #include <sys/stat.h>
35 #include <sys/param.h>
36 #include <cryptoutil.h>
37 #include "util.h"
38 
39 static int err; /* To store errno which may be overwritten by gettext() */
40 
41 int
42 kc_install(int argc, char *argv[])
43 {
44 	int 		rv = KC_OK;
45 	int		opt;
46 	extern int	optind_av;
47 	extern char	*optarg_av;
48 	char 		*keystore_name = NULL;
49 	char 		*modulepath = NULL;
50 	char		*option_str = NULL;
51 	conf_entry_t	*entry = NULL;
52 	char		realpath[MAXPATHLEN];
53 	struct stat 	statbuf;
54 	FILE		*pfile = NULL;
55 	FILE		*pfile_tmp = NULL;
56 	char		tmpfile_name[MAXPATHLEN];
57 	int		found_count = 0;
58 	char		buffer[BUFSIZ];
59 	char		*ptr;
60 	boolean_t 	found;
61 
62 	while ((opt = getopt_av(argc, argv, "k:(keystore)m:(modulepath)"
63 	    "o:(option)")) != EOF) {
64 		switch (opt) {
65 		case 'k':
66 			if (keystore_name != NULL)
67 				rv = KC_ERR_USAGE;
68 			else {
69 				keystore_name = get_string(optarg_av, &rv);
70 				if (keystore_name == NULL) {
71 					(void) fprintf(stderr, gettext(
72 					    "Error keystore input.\n"));
73 				}
74 			}
75 			break;
76 		case 'm':
77 			if (modulepath != NULL)
78 				rv = KC_ERR_USAGE;
79 			else {
80 				modulepath = get_string(optarg_av, &rv);
81 				if (modulepath == NULL) {
82 					(void) fprintf(stderr,
83 					    gettext("Error modulepath.\n"));
84 				}
85 			}
86 			break;
87 		case 'o':
88 			if (option_str != NULL) {
89 				rv = KC_ERR_USAGE;
90 			} else {
91 				option_str = get_string(optarg_av, &rv);
92 				if (option_str == NULL) {
93 					(void) fprintf(stderr,
94 					    gettext("Error option input.\n"));
95 				}
96 			}
97 			break;
98 		default:
99 			(void) fprintf(stderr,
100 			    gettext("Error input option.\n"));
101 			rv = KC_ERR_USAGE;
102 			break;
103 		}
104 		if (rv != KC_OK)
105 			goto out;
106 	}
107 
108 	/* No additional args allowed. */
109 	argc -= optind_av;
110 	if (argc) {
111 		(void) fprintf(stderr,
112 		    gettext("Error input option\n"));
113 		rv = KC_ERR_USAGE;
114 		goto out;
115 	}
116 
117 	if (keystore_name == NULL || modulepath == NULL) {
118 		(void) fprintf(stderr, gettext("Error input option\n"));
119 		rv = KC_ERR_USAGE;
120 		goto out;
121 	}
122 
123 	if (strcasecmp(keystore_name, "nss") == 0 ||
124 	    strcasecmp(keystore_name, "pkcs11") == 0 ||
125 	    strcasecmp(keystore_name, "file") == 0) {
126 		(void) fprintf(stderr,
127 		    gettext("Can not use the built-in keystore name %s\n"),
128 		    keystore_name);
129 		rv = KC_ERR_USAGE;
130 		goto out;
131 	}
132 
133 	entry = get_keystore_entry(keystore_name);
134 	if (entry != NULL) {
135 		(void) fprintf(stderr, gettext("%s exists already.\n"),
136 		    keystore_name);
137 		rv = KC_ERR_USAGE;
138 		goto out;
139 	}
140 
141 	/*
142 	 * Find the absolute path of the module and check if it exists in
143 	 * the system.  If $ISA is in the path, will check the 32bit version
144 	 * only.
145 	 */
146 	if (strncmp(modulepath, "/", 1) != 0) {
147 		/*
148 		 * Only contain the base name; prepand it with
149 		 * KMF_PLUGIN_PATH
150 		 */
151 		(void) snprintf(realpath, MAXPATHLEN, "%s%s",
152 		    KMF_PLUGIN_PATH, modulepath);
153 	} else {
154 		char *buf = modulepath;
155 		char *isa;
156 
157 		if ((isa = strstr(buf, PKCS11_ISA)) != NULL) {
158 			(void) strncpy(realpath, buf, isa - buf);
159 			isa += strlen(PKCS11_ISA) - 1;
160 			(void) strlcat(realpath, isa, MAXPATHLEN);
161 		} else {
162 			(void) strlcpy(realpath, modulepath, MAXPATHLEN);
163 		}
164 	}
165 
166 	if (stat(realpath, &statbuf) != 0) {
167 		(void) fprintf(stderr, gettext("%s not found.\n"),
168 		    realpath);
169 		rv = KC_ERR_ACCESS;
170 		goto out;
171 	}
172 
173 	if ((pfile = fopen(_PATH_KMF_CONF, "r+")) == NULL) {
174 		err = errno;
175 		(void) fprintf(stderr,
176 		    gettext("failed to update the configuration - %s\n"),
177 		    strerror(err));
178 		rv = KC_ERR_ACCESS;
179 		goto out;
180 	}
181 
182 	if (lockf(fileno(pfile), F_TLOCK, 0) == -1) {
183 		err = errno;
184 		(void) fprintf(stderr,
185 		    gettext("failed to lock the configuration - %s\n"),
186 		    strerror(err));
187 		rv = KC_ERR_INSTALL;
188 		goto out;
189 	}
190 
191 	/*
192 	 * Create a temporary file in the /etc/crypto directory.
193 	 */
194 	(void) strlcpy(tmpfile_name, CONF_TEMPFILE, sizeof (tmpfile_name));
195 	if (mkstemp(tmpfile_name) == -1) {
196 		err = errno;
197 		(void) fprintf(stderr,
198 		    gettext("failed to create a temporary file - %s\n"),
199 		    strerror(err));
200 		rv = KC_ERR_INSTALL;
201 		goto out;
202 	}
203 
204 	if ((pfile_tmp = fopen(tmpfile_name, "w")) == NULL) {
205 		err = errno;
206 		(void) fprintf(stderr,
207 		    gettext("failed to open %s - %s\n"),
208 		    tmpfile_name, strerror(err));
209 		rv = KC_ERR_INSTALL;
210 		goto out;
211 	}
212 
213 	/*
214 	 * Loop thru the config file. If the file was reserved within a
215 	 * package bracket, just uncomment it.  Other wise, append it at
216 	 * the end.  The resulting file will be saved in the temp file first.
217 	 */
218 	while (fgets(buffer, BUFSIZ, pfile) != NULL) {
219 		found = B_FALSE;
220 		if (buffer[0] == '#') {
221 			ptr = buffer;
222 			ptr++;
223 			while (*ptr == '#' || *ptr == ' ')
224 				ptr++;
225 			if (strncmp(keystore_name, ptr, strlen(keystore_name))
226 			    == 0) {
227 				found = B_TRUE;
228 				found_count++;
229 			}
230 		}
231 
232 		if (found == B_FALSE) {
233 			if (fputs(buffer, pfile_tmp) == EOF) {
234 				rv = KC_ERR_INSTALL;
235 				goto out;
236 			}
237 		} else {
238 			if (found_count == 1) {
239 				if (fputs(ptr, pfile_tmp) == EOF) {
240 					rv = KC_ERR_INSTALL;
241 					goto out;
242 				}
243 			} else {
244 				/*
245 				 * Found a second entry with #keystore_name.
246 				 * This should not happen. The kmf.conf file
247 				 * is corrupted. Give a warning and skip
248 				 * this entry.
249 				 */
250 				(void) fprintf(stderr, gettext(
251 				    "(Warning) Found an additional reserved "
252 				    "entry for %s.\n"), keystore_name);
253 			}
254 		}
255 	}
256 
257 	if (found_count == 0) {
258 		char buf[MAXPATHLEN];
259 		/*
260 		 * This entry was not in package before, append it to the
261 		 * end of the temp file.
262 		 */
263 		if (option_str == NULL)
264 			(void) snprintf(buf, MAXPATHLEN, "%s:%s%s\n",
265 			    keystore_name, CONF_MODULEPATH, modulepath);
266 		else
267 			(void) snprintf(buf, MAXPATHLEN, "%s:%s%s;%s%s\n",
268 			    keystore_name, CONF_MODULEPATH, modulepath,
269 			    CONF_OPTION, option_str);
270 
271 		if (fputs(buf, pfile_tmp) == EOF) {
272 			err = errno;
273 			(void) fprintf(stderr, gettext(
274 			    "failed to write to %s: %s\n"), tmpfile_name,
275 			    strerror(err));
276 			rv = KC_ERR_INSTALL;
277 			goto out;
278 		}
279 	}
280 
281 out:
282 	if (pfile != NULL)
283 		(void) fclose(pfile);
284 
285 	if (rv != KC_OK && pfile_tmp != NULL)
286 		(void) unlink(tmpfile_name);
287 
288 	if (pfile_tmp != NULL)
289 		(void) fclose(pfile_tmp);
290 
291 	if (rv == KC_OK) {
292 		if (rename(tmpfile_name, _PATH_KMF_CONF) == -1) {
293 			err = errno;
294 			(void) fprintf(stderr, gettext(
295 			    "failed to update the configuration - %s"),
296 			    strerror(err));
297 			return (KC_ERR_INSTALL);
298 		}
299 
300 		if (chmod(_PATH_KMF_CONF,
301 		    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
302 			err = errno;
303 			(void) fprintf(stderr, gettext(
304 			    "failed to update the configuration - %s\n"),
305 			    strerror(err));
306 			return (KC_ERR_INSTALL);
307 		}
308 	}
309 
310 	return (rv);
311 }
312