1 /* $Id: chngproc.c,v 1.4 2016/09/01 00:35:21 florian 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 int rc; 33 long lval; 34 enum chngop op; 35 char *tok, *th, *fmt; 36 char **fs; 37 size_t i, fsz; 38 void *pp; 39 int fd, cc; 40 41 rc = 0; 42 th = tok = fmt = NULL; 43 fd = -1; 44 fs = NULL; 45 fsz = 0; 46 47 if (chroot(root) == -1) { 48 warn("chroot"); 49 goto out; 50 } 51 if (chdir("/") == -1) { 52 warn("chdir"); 53 goto out; 54 } 55 if (pledge("stdio cpath wpath", NULL) == -1) { 56 warn("pledge"); 57 goto out; 58 } 59 60 /* 61 * Loop while we wait to get a thumbprint and token. 62 * We'll get this for each SAN request. 63 */ 64 65 for (;;) { 66 op = CHNG__MAX; 67 if (0 == (lval = readop(netsock, COMM_CHNG_OP))) 68 op = CHNG_STOP; 69 else if (CHNG_SYN == lval) 70 op = lval; 71 72 if (CHNG__MAX == op) { 73 warnx("unknown operation from netproc"); 74 goto out; 75 } else if (CHNG_STOP == op) 76 break; 77 78 assert(CHNG_SYN == op); 79 80 /* 81 * Read the thumbprint and token. 82 * The token is the filename, so store that in a vector 83 * of tokens that we'll later clean up. 84 */ 85 86 if (NULL == (th = readstr(netsock, COMM_THUMB))) 87 goto out; 88 else if (NULL == (tok = readstr(netsock, COMM_TOK))) 89 goto out; 90 91 /* Vector appending... */ 92 93 pp = realloc(fs, (fsz + 1) * sizeof(char *)); 94 if (NULL == pp) { 95 warn("realloc"); 96 goto out; 97 } 98 fs = pp; 99 fs[fsz] = tok; 100 tok = NULL; 101 fsz++; 102 103 if (-1 == asprintf(&fmt, "%s.%s", fs[fsz - 1], th)) { 104 warn("asprintf"); 105 goto out; 106 } 107 108 /* 109 * I use this for testing when letskencrypt is being run 110 * on machines apart from where I'm hosting the 111 * challenge directory. 112 * DON'T DEPEND ON THIS FEATURE. 113 */ 114 if (remote) { 115 puts("RUN THIS IN THE CHALLENGE DIRECTORY"); 116 puts("YOU HAVE 20 SECONDS..."); 117 printf("doas sh -c \"echo %s > %s\"\n", 118 fmt, fs[fsz - 1]); 119 sleep(20); 120 puts("TIME'S UP."); 121 } else { 122 /* 123 * Create and write to our challenge file. 124 * Note: we use file descriptors instead of FILE 125 * because we want to minimise our pledges. 126 */ 127 fd = open(fs[fsz - 1], 128 O_WRONLY|O_EXCL|O_CREAT, 0444); 129 if (-1 == fd) { 130 warn("%s", fs[fsz - 1]); 131 goto out; 132 } if (-1 == write(fd, fmt, strlen(fmt))) { 133 warn("%s", fs[fsz - 1]); 134 goto out; 135 } else if (-1 == close(fd)) { 136 warn("%s", fs[fsz - 1]); 137 goto out; 138 } 139 fd = -1; 140 } 141 142 free(th); 143 free(fmt); 144 th = fmt = NULL; 145 146 dodbg("%s/%s: created", root, fs[fsz - 1]); 147 148 /* 149 * Write our acknowledgement. 150 * Ignore reader failure. 151 */ 152 153 cc = writeop(netsock, COMM_CHNG_ACK, CHNG_ACK); 154 if (0 == cc) 155 break; 156 if (cc < 0) 157 goto out; 158 } 159 160 rc = 1; 161 out: 162 close(netsock); 163 if (-1 != fd) 164 close(fd); 165 for (i = 0; i < fsz; i++) { 166 if (-1 == unlink(fs[i]) && ENOENT != errno) 167 warn("%s", fs[i]); 168 free(fs[i]); 169 } 170 free(fs); 171 free(fmt); 172 free(th); 173 free(tok); 174 return(rc); 175 } 176