1 /* $Id: chngproc.c,v 1.7 2016/09/13 17:13:37 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <assert.h> 19 #include <err.h> 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include "extern.h" 28 29 int 30 chngproc(int netsock, const char *root, int remote) 31 { 32 char *tok = NULL, *th = NULL, *fmt = NULL, **fs = NULL; 33 size_t i, fsz = 0; 34 int rc = 0, fd = -1, cc; 35 long lval; 36 enum chngop op; 37 void *pp; 38 39 if (chroot(root) == -1) { 40 warn("chroot"); 41 goto out; 42 } 43 if (chdir("/") == -1) { 44 warn("chdir"); 45 goto out; 46 } 47 if (pledge("stdio cpath wpath", NULL) == -1) { 48 warn("pledge"); 49 goto out; 50 } 51 52 /* 53 * Loop while we wait to get a thumbprint and token. 54 * We'll get this for each SAN request. 55 */ 56 57 for (;;) { 58 op = CHNG__MAX; 59 if (0 == (lval = readop(netsock, COMM_CHNG_OP))) 60 op = CHNG_STOP; 61 else if (CHNG_SYN == lval) 62 op = lval; 63 64 if (CHNG__MAX == op) { 65 warnx("unknown operation from netproc"); 66 goto out; 67 } else if (CHNG_STOP == op) 68 break; 69 70 assert(CHNG_SYN == op); 71 72 /* 73 * Read the thumbprint and token. 74 * The token is the filename, so store that in a vector 75 * of tokens that we'll later clean up. 76 */ 77 78 if (NULL == (th = readstr(netsock, COMM_THUMB))) 79 goto out; 80 else if (NULL == (tok = readstr(netsock, COMM_TOK))) 81 goto out; 82 83 /* Vector appending... */ 84 85 pp = reallocarray(fs, (fsz + 1), sizeof(char *)); 86 if (NULL == pp) { 87 warn("realloc"); 88 goto out; 89 } 90 fs = pp; 91 fs[fsz] = tok; 92 tok = NULL; 93 fsz++; 94 95 if (-1 == asprintf(&fmt, "%s.%s", fs[fsz - 1], th)) { 96 warn("asprintf"); 97 goto out; 98 } 99 100 /* 101 * I use this for testing when letskencrypt is being run 102 * on machines apart from where I'm hosting the 103 * challenge directory. 104 * DON'T DEPEND ON THIS FEATURE. 105 */ 106 if (remote) { 107 puts("RUN THIS IN THE CHALLENGE DIRECTORY"); 108 puts("YOU HAVE 20 SECONDS..."); 109 printf("doas sh -c \"echo %s > %s\"\n", 110 fmt, fs[fsz - 1]); 111 sleep(20); 112 puts("TIME'S UP."); 113 } else { 114 /* 115 * Create and write to our challenge file. 116 * Note: we use file descriptors instead of FILE 117 * because we want to minimise our pledges. 118 */ 119 fd = open(fs[fsz - 1], O_WRONLY|O_EXCL|O_CREAT, 0444); 120 if (-1 == fd) { 121 warn("%s", fs[fsz - 1]); 122 goto out; 123 } if (-1 == write(fd, fmt, strlen(fmt))) { 124 warn("%s", fs[fsz - 1]); 125 goto out; 126 } else if (-1 == close(fd)) { 127 warn("%s", fs[fsz - 1]); 128 goto out; 129 } 130 fd = -1; 131 } 132 133 free(th); 134 free(fmt); 135 th = fmt = NULL; 136 137 dodbg("%s/%s: created", root, fs[fsz - 1]); 138 139 /* 140 * Write our acknowledgement. 141 * Ignore reader failure. 142 */ 143 144 cc = writeop(netsock, COMM_CHNG_ACK, CHNG_ACK); 145 if (0 == cc) 146 break; 147 if (cc < 0) 148 goto out; 149 } 150 151 rc = 1; 152 out: 153 close(netsock); 154 if (-1 != fd) 155 close(fd); 156 for (i = 0; i < fsz; i++) { 157 if (-1 == unlink(fs[i]) && ENOENT != errno) 158 warn("%s", fs[i]); 159 free(fs[i]); 160 } 161 free(fs); 162 free(fmt); 163 free(th); 164 free(tok); 165 return(rc); 166 } 167