1 #define _POSIX_C_SOURCE 200809L
5 #include <sys/socket.h>
6 #include <netinet/in.h>
28 int tls_parse_uri(char *, struct tls_uri *);
29 void tls_free_uri(struct tls_uri *);
31 int open_tcp_connection(char *host, int port);
33 /* if trustpolicy is 0, we just accept anything */
34 int verify_trust(struct TLSContext *context, struct TLSCertificate **chain, int
37 if (context || chain || len) {
44 static char hexchars[] = "0123456789abcdefABCDEF";
46 static void hex(char *dst, uint8_t *src, size_t len) {
48 dst[0] = hexchars[(src[0]>>4)&0xf];
49 dst[1] = hexchars[src[0]&0xf];
56 static void hexbin(uint8_t *dst, unsigned char *src, size_t len) {
60 for (i=0; i<len; i+=2) {
61 sscanf((const char *)src+i, "%02x", &x);
67 char *my_getline(struct tls_buffer *b, int fd, size_t *size) {
72 while (b->error == 0) {
73 loc = memchr(b->buffer, '\n', b->len);
75 *size = loc - b->buffer + 1;
78 rv = read(fd, buf, sizeof buf);
85 tls_buffer_append(b, buf, rv);
96 * We use a trust on first use policy. The trust DB is a simple
97 * file in /var/lib/zpm/known_hosts, or ~/.zpm/known_hosts, or ZPM_KNOWNHOSTS.
98 * if -k is given, no verification is done
100 int verify_first(struct TLSContext *context, struct TLSCertificate **chain, int
103 char *trustfile, *homedir = 0, *host, *fp;
104 unsigned char certhash[65];
106 struct tls_buffer tbuf;
111 if (certs == 0 || chain == 0) {
115 err = tls_certificate_is_valid(chain[0]);
121 err = tls_certificate_valid_subject(chain[0], context->sni);
127 hex(certhash, chain[0]->fp, 32);
130 trustfile = getenv("ZPM_KNOWNHOSTS");
132 if (geteuid() == 0) {
133 trustfile = "/var/lib/zpm/known_hosts";
135 homedir = getenv("HOME");
139 len = snprintf(homedir, 0, "%s/.zpm/known_hosts", homedir);
140 homedir = malloc(len+1);
144 len = snprintf(homedir, len+1, "%s/.zpm/known_hosts", homedir);
148 /* cert is valid on its face, so check against the trust db */
149 trustdb = open(trustfile, O_RDWR|O_CREAT, 0600);
154 fprintf(stderr, "cannot open trustdb: %s\n", strerror(errno));
159 tls_buffer_init(&tbuf, 128);
163 tls_buffer_shift(&tbuf, len);
164 line = my_getline(&tbuf, trustdb, &len);
171 while (isspace(*fp)) {
177 off = strchr(line, ':');
183 if (line[len-1] == '\n') {
187 if (strlen(fp) != 64) {
191 if (len && line[len-1] == '\n') {
195 if (strcmp(context->sni, host) != 0) {
199 int match = (memcmp(certhash, fp, 64) == 0);
202 tls_buffer_free(&tbuf);
203 return match ? no_error : bad_certificate;
204 } while (!tbuf.error);
206 /* got here, so we should be at EOF, so add this host to trust db */
207 lseek(trustdb, 0, SEEK_END);
208 write(trustdb, certhash, 64);
209 write(trustdb, ":", 1);
210 write(trustdb, context->sni, strlen(context->sni));
211 write(trustdb, "\n", 1);
213 tls_buffer_free(&tbuf);
218 int verify_roots(struct TLSContext *context, struct TLSCertificate **chain, int
223 for (i = 0; i < len; i++) {
224 struct TLSCertificate *certificate = chain[i];
225 // check validity date
226 err = tls_certificate_is_valid(certificate);
230 // check certificate in certificate->bytes of length
231 // certificate->len the certificate is in ASN.1 DER
235 // check if chain is valid
236 err = tls_certificate_chain_is_valid(chain, len);
241 if (len > 0 && context->sni) {
242 err = tls_certificate_valid_subject(chain[0], context->sni);
248 /* Perform certificate validation against ROOT CA */
249 err = tls_certificate_chain_is_valid_root(context, chain, len);
258 struct tls_buffer response;
259 struct TLSContext *tls;
262 time_t last_modified;
264 size_t content_length;
269 char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
270 "Aug", "Sep", "Oct", "Nov", "Dec" };
273 for (i=0; i < 12; i++) {
274 if (!strncasecmp(m, months[i], 3)) {
281 /* Wed, 06 Feb 2019 10:06:05 GMT */
282 time_t parse_date(char *d) {
283 //static const char format[] = "%a, %d %b %Y %H:%M:%S %Z"; // rfc 1123
284 struct tm tm = { 0 };
287 //char *data = "Tue, 13 Dec 2011 16:08:21 GMT";
290 rv = sscanf(d, "%*[a-zA-Z,] %d %s %d %d:%d:%d", &dom, M, &Y, &h, &m, &s);
292 tm.tm_year = Y - 1900;
296 tm.tm_mon = month(M)-1;
304 char *find_header(struct io *io, char *header, size_t *len) {
310 hlen = strlen(header);
311 eoh = strstr(io->response.buffer, "\r\n\r\n");
315 soh = io->response.buffer;
317 soh = strstr(soh, "\r\n");
322 if (!memcmp(soh, header, hlen)) {
330 eoh = strstr(soh, "\r\n");
332 while (soh < eoh && isspace(*soh)) {
339 void parse_header(struct io *io) {
340 char *s = io->response.buffer;
345 while (!isspace(*s)) {
348 while (isspace(*s)) {
351 code = strtol(s, 0, 10);
352 io->status_code = code;
354 hval = find_header(io, "Date:", &hlen);
357 io->date = parse_date(hval);
360 hval = find_header(io, "Last-Modified:", &hlen);
363 io->last_modified = parse_date(hval);
367 hval = find_header(io, "Content-Length:", &hlen);
370 io->content_length = strtoul(hval, 0, 10);
379 hval = find_header(io, "Location:", &hlen);
381 io->redirect = strndup(hval, hlen);
390 ssize_t fill_buffer(struct io *io) {
391 unsigned char buffer[4096];
395 ret = tls_read(io->tls, buffer, sizeof buffer);
397 ret = read(io->socket, buffer, sizeof buffer);
401 tls_buffer_append(&io->response, buffer, ret);
408 char *nextline(struct io *io) {
411 eol = memchr(io->response.buffer, '\n', io.response.size);
414 eol = memchr(io->response.buffer, '\n', io.response.size);
421 void append_header(struct tls_buffer *buf, char *header, char *val) {
422 tls_buffer_append(buf, header, strlen(header));
423 tls_buffer_append(buf, ": ", 2);
424 tls_buffer_append(buf, val, strlen(val));
425 tls_buffer_append(buf, "\r\n", 2);
428 static void pdots(int len, int ch, unsigned long was, unsigned long now,
429 unsigned long total) {
430 was = len * was / total;
434 now = len * now / total;
435 while (was++ < now) {
440 int main(int ac, char *av[]) {
441 int sockfd, port = -1, rv;
445 char msg[] = "GET %s HTTP/1.1\r\nHost: %s:%i\r\nConnection: close\r\n\r\n";
446 char msg2[] = "GET %s HTTP/1.1\r\nHost: %s:%i\r\nLast-Modified: %s\r\nConnection: close\r\n\r\n";
447 char msg_buffer[1024];
453 int raw = 0, head = 0;
456 struct io io = { {0}, 0, -1, 0, 0, 0, 0, 0 };
457 struct TLSContext *clientssl = 0;
461 struct tls_buffer request;
467 int redirs = 0, redirlimit = 50, printstatus = 0;
468 int verifypolicy = 1;
472 while ((option = getopt(ac, av, "o:rIfz:#R:SkK")) != -1) {
474 case 'o': outfile = optarg; break;
475 case 'S': printstatus = 1; head = 1; break;
476 case 'k': verifypolicy = 0; break;
477 case 'K': verifypolicy = 2; break;
479 case 'r': raw = 1; break;
480 case 'f': failsilent = 1; break;
481 case 'z': lmfile = optarg; break;
482 case 'R': redirlimit = strtol(optarg, 0, 10); break;
483 case '#': progressbar = 1; break;
491 fprintf(stderr, "Usage: %s uri\n", av[0]);
501 rv = stat(lmfile, &st);
503 perror("stat failed:");
508 strftime(lmtime, sizeof lmtime, "%a, %d %b %Y %H:%M:%S GMT", mtime);
511 url = strdup(av[optind]);
517 out = open(outfile, O_WRONLY|O_CREAT, 0600);
519 perror("can't open output file:");
524 signal(SIGPIPE, SIG_IGN);
526 tls_buffer_init(&io.response, 0);
527 tls_buffer_init(&request, 128);
529 while (redirs++ <= redirlimit) {
534 tls_parse_uri(url, &uri);
536 port = atoi(uri.port);
539 /* construct request */
541 tls_buffer_append(&request, "HEAD ", 5);
543 tls_buffer_append(&request, "GET ", 4);
545 tls_buffer_append(&request, uri.path, strlen(uri.path));
546 tls_buffer_append(&request, " HTTP/1.1", 9);
547 tls_buffer_append(&request, "\r\n", 2);
549 append_header(&request, "Host", host);
550 append_header(&request, "Connection", "close");
552 append_header(&request, "If-Modified-Since", lmtime);
554 tls_buffer_append(&request, "\r\n", 2);
555 //fprintf(stderr, "msg =\n%.*s", (int)request.len, request.buffer);
557 if (!strcmp(uri.scheme, "https")) {
560 clientssl = tls_create_context(TLS_CLIENT, TLS_V12);
562 /* optionally, we can set a certificate validation
563 * callback function if set_verify is not called, and
564 * root ca is set, `tls_default_verify` will be used
565 * (does exactly what `verify` does in this example)
567 if (verifypolicy == 2) {
569 cert_path = getenv("ZPM_CERTFILE");
571 cert_path = "/var/lib/zpm/roots.pem";
573 rv = tls_load_root_file(clientssl, cert_path);
575 fprintf(stderr, "Error loading root certs\n");
578 tls_set_verify(clientssl, verify_roots);
579 } else if (verifypolicy == 1) {
580 tls_set_verify(clientssl, verify_first);
582 tls_set_verify(clientssl, verify_trust);
586 fprintf(stderr, "Error initializing client context\n");
589 tls_sni_set(clientssl, uri.host);
594 sockfd = open_tcp_connection(host, port);
602 tls_set_fd(clientssl, sockfd);
604 if ((rv = tls_connect(clientssl)) != 1) {
605 fprintf(stderr, "Handshake Error %i\n", rv);
609 ret = tls_write(clientssl, request.buffer, request.len);
611 ret = write(io.socket, request.buffer, request.len);
615 fprintf(stderr, "write error %zd\n", ret);
620 ret = fill_buffer(&io);
624 eoh = strstr(io.response.buffer, "\r\n\r\n");
631 /* never got (complet) header */
632 fprintf(stderr, "incomplete response to %s\n", av[optind]);
636 header_len = (size_t)(eoh - io.response.buffer) + 4;
639 switch (io.status_code) {
645 url = strdup(io.redirect);
651 printf("%d\n", io.status_code);
655 if (io.status_code != 200) {
660 tls_buffer_shift(&io.response, header_len);
663 io.response.len -= 2;
667 if (io.content_length) {
668 fprintf(stderr, "(%lu) ", io.content_length);
673 write(out, io.response.buffer, io.response.len);
674 ret = io.response.len;
678 if (io.content_length) {
679 pdots(50, '.', total, total+ret,
682 int old = total / 1000000;
683 int new = (total+ret)/1000000;
690 ret = fill_buffer(&io);
694 fprintf(stderr, "%s read error %zd\n", uri.scheme, ret);
696 /* futimens(out, ...) */
698 tls_buffer_free(&io.response);
703 tls_shutdown(clientssl);
708 if (progressbar && io.status_code == 200) {
709 fprintf(stderr, "(%lu)", total);
713 return io.status_code == 200 ? 0 : EXIT_FAILURE;