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 /* 23 * Copyright 2012 Joshua M. Clulow <josh@sysmgr.org> 24 */ 25 26 #include <stdlib.h> 27 #include <string.h> 28 #include <strings.h> 29 #include <errno.h> 30 #include <unistd.h> 31 #include <limits.h> 32 #include <fcntl.h> 33 #include <err.h> 34 #include <locale.h> 35 #include <libintl.h> 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 #include <sys/wait.h> 39 #include <dirent.h> 40 #include <stddef.h> 41 #include <libscf.h> 42 43 #include "utils.h" 44 #include "scf.h" 45 46 #ifndef TEXT_DOMAIN 47 #define TEXT_DOMAIN "SYS_TEST" 48 #endif 49 50 #define _(x) gettext(x) 51 52 #define CERTS_FILE_DIR "/etc/certs/CA" 53 #define CERTS_LINK_DIR "/etc/openssl/certs" 54 55 #define OPENSSL "/usr/bin/openssl" 56 57 #define FMT_VERSION "OpenSSL Version: %s (%s)\n" 58 #define MSG_OLD_HASH "supports old hash only" 59 #define MSG_NEW_HASH "supports new hashes" 60 61 #define FMT_WRONG "WRONG: [%s] -> %s\n should be: %s\n" 62 #define FMT_MISSING "MISSING: [%s] -> %s\n" 63 #define FMT_DANGLING "DANGLING: [%s] -> %s\n" 64 #define FMT_UNKNOWN "UNKNOWN TARGET: [%s] -> %s\n" 65 66 #define MAX_HASHES 2 67 68 typedef struct cert_file { 69 char *path; 70 char *hash[MAX_HASHES]; 71 uint8_t hashcnt; 72 73 struct cert_file *next; 74 } cert_file_t; 75 76 typedef struct cert_link { 77 char *path; 78 char *hash; 79 uint8_t targetfound; 80 81 struct cert_link *next; 82 } cert_link_t; 83 84 /* configuration from smf(5) */ 85 static char *certs_file_dir = CERTS_FILE_DIR; 86 static char *certs_link_dir = CERTS_LINK_DIR; 87 static char *openssl = OPENSSL; 88 static int verbose = B_FALSE; 89 static boolean_t remove_dangling = B_TRUE; 90 static boolean_t remove_unknown = B_FALSE; 91 static boolean_t create_missing = B_TRUE; 92 static boolean_t correct_wrong = B_TRUE; 93 94 /* list heads */ 95 static cert_file_t *file0 = NULL; 96 static cert_link_t *link0 = NULL; 97 98 #define OP_HASH 0 99 #define OP_VERSION 1 100 #define OP_HASH_OLD 2 101 #define NUM_OPS 3 102 char * 103 run_openssl(unsigned int op, char *arg) 104 { 105 pid_t pid; 106 int des[2]; 107 int i; 108 ssize_t num; 109 char buf[500]; 110 int status; 111 112 if (op >= NUM_OPS) 113 abort(); 114 115 (void) pipe(des); 116 switch (pid = fork()) { 117 case -1: /* error */ 118 err(SMF_EXIT_ERR_FATAL, _("forking child for openssl")); 119 break; 120 case 0: /* child */ 121 (void) close(des[0]); 122 if (dup2(des[1], STDOUT_FILENO) == -1 || dup2(des[1], 123 STDERR_FILENO) == -1) 124 err(100, NULL); 125 (void) close(STDIN_FILENO); 126 switch (op) { 127 case OP_HASH: 128 execlp(openssl, openssl, "x509", "-hash", 129 "-noout", "-in", arg, (char *)0); 130 break; 131 case OP_VERSION: 132 execlp(openssl, openssl, "version", (char *)0); 133 break; 134 case OP_HASH_OLD: 135 execlp(openssl, openssl, "x509", 136 "-subject_hash_old", "-noout", "-in", 137 arg, (char *)0); 138 break; 139 } 140 printf(_("exec openssl failed: %s\n"), strerror(errno)); 141 exit(SMF_EXIT_ERR_FATAL); 142 break; 143 default: /* parent */ 144 (void) close(des[1]); 145 num = read(des[0], &buf, sizeof (buf)); 146 (void) close(des[0]); 147 148 if (num < 0) 149 err(101, _("reading from openssl child")); 150 151 buf[num] = 0; 152 for (i = 0; i < num; i++) { 153 if (buf[i] == '\n' || buf[i] == '\r') { 154 buf[i] = 0; 155 break; 156 } 157 } 158 159 if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status)) 160 err(SMF_EXIT_ERR_FATAL, 161 _("waiting for openssl child")); 162 if (WEXITSTATUS(status) != 0) 163 errx(SMF_EXIT_ERR_FATAL, 164 _("error from openssl child (rc: %d): %s"), 165 WEXITSTATUS(status), buf); 166 167 return (xstrdup(buf)); 168 } 169 } 170 171 int newossl = 0; 172 173 174 void 175 detect_openssl(void) 176 { 177 char *vers; 178 179 /* determine openssl version */ 180 vers = run_openssl(OP_VERSION, NULL); 181 if (strstr(vers, "OpenSSL 1.") == vers) 182 newossl = 1; 183 else if (strstr(vers, "OpenSSL 0.9.") != vers) 184 errx(SMF_EXIT_ERR_FATAL, _("unrecognised OpenSSL: %s"), vers); 185 if (verbose) 186 printf(_(FMT_VERSION), vers, (newossl ? _(MSG_NEW_HASH) : 187 _(MSG_OLD_HASH))); 188 free(vers); 189 } 190 191 void 192 store_certlink(cert_link_t *cd) 193 { 194 if (link0 == NULL) { 195 link0 = cd; 196 } else { 197 cert_link_t *t = link0; 198 while (t->next != NULL) 199 t = t->next; 200 t->next = cd; 201 } 202 } 203 204 cert_link_t * 205 find_link_by_hash(char *hash) 206 { 207 cert_link_t *t = link0; 208 while (t != NULL) { 209 if (strcmp(t->hash, hash) == 0) 210 return (t); 211 t = t->next; 212 } 213 return (NULL); 214 } 215 216 void 217 store_certfile(cert_file_t *cs) 218 { 219 if (file0 == NULL) { 220 file0 = cs; 221 } else { 222 cert_file_t *t = file0; 223 while (t->next != NULL) 224 t = t->next; 225 t->next = cs; 226 } 227 } 228 229 char * 230 add_dot_zero(char *str) 231 { 232 char *ret; 233 xasprintf(&ret, "%s.0", str); 234 free(str); 235 return (ret); 236 } 237 238 void 239 populate_hash_list(cert_file_t *cs) 240 { 241 if (cs->path[0] != '/') 242 abort(); 243 244 if (newossl) 245 cs->hash[cs->hashcnt++] = 246 add_dot_zero(run_openssl(OP_HASH_OLD, cs->path)); 247 cs->hash[cs->hashcnt++] = add_dot_zero(run_openssl(OP_HASH, cs->path)); 248 } 249 250 void 251 get_cert_file_list(void) 252 { 253 DIR *dir; 254 struct dirent *de; 255 256 dir = opendir(certs_file_dir); 257 if (dir == NULL) 258 err(SMF_EXIT_ERR_FATAL, _("could not read directory %s"), 259 certs_file_dir); 260 261 for (de = readdir(dir); de != NULL; de = readdir(dir)) { 262 char *a; 263 cert_file_t *cs; 264 265 if (strcmp(".", de->d_name) == 0 || 266 strcmp("..", de->d_name) == 0) 267 continue; 268 269 cs = xmalloc(sizeof (*cs)); 270 xasprintf(&a, "%s/%s", certs_file_dir, de->d_name); 271 cs->path = xrealpath(a); 272 free(a); 273 populate_hash_list(cs); 274 store_certfile(cs); 275 } 276 (void) closedir(dir); 277 } 278 279 void 280 removehashlink(char *hash) 281 { 282 char *fqhash; 283 xasprintf(&fqhash, "%s/%s", certs_link_dir, hash); 284 xunlink(fqhash); 285 free(fqhash); 286 } 287 288 void 289 fixhashlink(char *file, char *hash) 290 { 291 char *fqhash; 292 char *relfile; 293 294 xasprintf(&fqhash, "%s/%s", certs_link_dir, hash); 295 relfile = xmakerelative(certs_link_dir, file); 296 297 if (xexists(fqhash)) 298 xunlink(fqhash); 299 300 if (symlink(relfile, fqhash) != 0) 301 err(SMF_EXIT_ERR_FATAL, _("could not create symlink %s"), 302 fqhash); 303 304 free(fqhash); 305 free(relfile); 306 } 307 308 void 309 get_cert_link_list(void) 310 { 311 DIR *dir; 312 struct dirent *de; 313 314 dir = opendir(certs_link_dir); 315 if (dir == NULL) 316 err(SMF_EXIT_ERR_FATAL, _("could not read directory %s"), 317 certs_link_dir); 318 319 for (de = readdir(dir); de != NULL; de = readdir(dir)) { 320 cert_link_t *cd; 321 char *a, *b; 322 323 if (strcmp(".", de->d_name) == 0 || 324 strcmp("..", de->d_name) == 0) 325 continue; 326 327 cd = xmalloc(sizeof (*cd)); 328 329 cd->hash = xstrdup(de->d_name); 330 331 xasprintf(&a, "%s/%s", certs_link_dir, de->d_name); 332 b = xreadlink(a); 333 free(a); 334 if (b[0] == '.') { 335 xasprintf(&a, "%s/%s", certs_link_dir, b); 336 free(b); 337 b = a; 338 } 339 a = xrealpath(b); 340 free(b); 341 cd->path = a; 342 343 store_certlink(cd); 344 } 345 (void) closedir(dir); 346 } 347 348 void 349 read_config(void) 350 { 351 int a; 352 char *b; 353 354 a = get_config_boolean("verbose"); 355 if (a >= 0) 356 verbose = a; 357 a = get_config_boolean("remove_dangling"); 358 if (a >= 0) 359 remove_dangling = a; 360 a = get_config_boolean("remove_unknown"); 361 if (a >= 0) 362 remove_unknown = a; 363 a = get_config_boolean("create_missing"); 364 if (a >= 0) 365 create_missing = a; 366 a = get_config_boolean("correct_wrong"); 367 if (a >= 0) 368 correct_wrong = a; 369 370 b = get_config_string("certs_file_dir"); 371 if (b != NULL) 372 certs_file_dir = b; 373 b = get_config_string("certs_link_dir"); 374 if (b != NULL) 375 certs_link_dir = b; 376 b = get_config_string("openssl_command"); 377 if (b != NULL) 378 openssl = b; 379 } 380 381 382 int 383 main(void) 384 { 385 cert_file_t *tf; 386 cert_link_t *tl; 387 388 (void) setlocale(LC_ALL, ""); 389 (void) textdomain(TEXT_DOMAIN); 390 391 if (init_scf() != 0) 392 errx(SMF_EXIT_ERR_FATAL, _("could not connect to smf(5): %s"), 393 scf_strerror(scf_error())); 394 read_config(); 395 fini_scf(); 396 397 #ifdef DEBUG 398 verbose = B_TRUE; 399 #endif 400 401 detect_openssl(); 402 403 get_cert_link_list(); 404 get_cert_file_list(); 405 406 /* 407 * look at each certificate file and determine if there is 408 * a symlink to it in the link directory 409 */ 410 for (tf = file0; tf != NULL; tf = tf->next) { 411 int j; 412 for (j = 0; j < tf->hashcnt; j++) { 413 tl = find_link_by_hash(tf->hash[j]); 414 if (tl) { 415 tl->targetfound = 1; 416 if (correct_wrong && strcmp(tl->path, tf->path) 417 != 0) { 418 if (verbose) 419 printf(_(FMT_WRONG), 420 tf->hash[j], tl->path, 421 tf->path); 422 fixhashlink(tf->path, tf->hash[j]); 423 } 424 } else if (create_missing) { 425 if (verbose) 426 printf(_(FMT_MISSING), tf->hash[j], 427 tf->path); 428 fixhashlink(tf->path, tf->hash[j]); 429 } 430 } 431 } 432 /* 433 * look at each certificate link and determine if it's dangling or 434 * pointing to a certificate file which we haven't seen 435 */ 436 for (tl = link0; tl != NULL; tl = tl->next) { 437 if (!xexists(tl->path)) { 438 if (remove_dangling) { 439 if (verbose) 440 printf(_(FMT_DANGLING), tl->hash, 441 tl->path); 442 removehashlink(tl->hash); 443 } 444 } else if (!tl->targetfound) { 445 if (remove_unknown) { 446 if (verbose) 447 printf(_(FMT_UNKNOWN), tl->hash, 448 tl->path); 449 removehashlink(tl->hash); 450 } 451 } 452 } 453 454 return (0); 455 }