shithub: acmed

Download patch

ref: a5bb74cb87d260c60c2abf2828e5b1aa8a315225
parent: 9084556e8c9bf6b9556b1397ebbbbd6fd764e3c4
author: Ori Bernstein <ori@eigenstate.org>
date: Sat Sep 11 13:54:29 EDT 2021

dns support: busted

--- a/acmed.c
+++ b/acmed.c
@@ -21,11 +21,11 @@
 #define Contenttype	"contenttype application/jose+json"
 #define between(x,min,max)	(((min-1-x) & (x-max-1))>>8)
 int	debug;
-int	(*challengefn)(JSON*, char*, int*);
+int	(*challengefn)(char*, char*, int*);
 char	*keyspec;
 char	*provider = "https://acme-v02.api.letsencrypt.org/directory"; /* test endpoint */
-char	*challengedir = "/usr/web/.well-known/acme-challenge";
-char	*challengecmd;
+char	*challengeout;
+char	*challengedom;
 char	*keyid;
 char	*epnewnonce;
 char	*epnewacct;
@@ -439,11 +439,65 @@
 }
 
 static int
-httpchallenge(JSON *j, char *authurl, int *matched)
+httpchallenge(char *ty, char *tok, int *matched)
 {
+	char path[1024];
+	int fd, r;
+
+	if(strcmp(ty, "http-01") != 0)
+		return -1;
+	*matched = 1;
+	snprint(path, sizeof(path), "%s/%s", challengeout, tok);
+	if((fd = create(path, OWRITE, 0666)) == -1)
+		return -1;
+	r = fprint(fd, "%s.%s\n", tok, jwsthumb);
+	close(fd);
+	return r;
+}
+
+static int
+dnschallenge(char *ty, char *tok, int *matched)
+{
+	char *enc, auth[1024], hash[SHA2_256dlen];
+	int fd, r;
+
+	if(strcmp(ty, "dns-01") != 0)
+		return -1;
+	*matched = 1;
+	if(challengedom == nil){
+		werrstr("dns challenge requires domain");
+		return -1;
+	}
+
+	r = -1;
+	fd = -1;
+	snprint(auth, sizeof(auth), "%s.%s\n", tok, jwsthumb);
+	sha2_256((uchar*)auth, strlen(auth), (uchar*)hash, nil);
+	if((enc = encurl64(hash, sizeof(hash))) == nil)
+		goto Error;
+	if((fd = create(challengeout, OWRITE, 0666)) == -1)
+		goto Error;
+	if(fprint(fd,"dom=_acme-challenge.%s soa=\n\ttxtrr=%s\n", challengedom, enc) == -1)
+		goto Error;
+	if((fd = open("/net/dns", OWRITE)) != -1)
+		goto Error;
+	if(fprint(fd, "refresh") == -1)
+		goto Error;
+	r = 0;
+
+Error:
+	if(fd != -1)
+		close(fd);
+	free(enc);
+	return r;
+}
+
+static int
+challenge(JSON *j, char *authurl, int *matched)
+{
 	JSON *ty, *url, *tok, *poll, *state;
-	char *resp, path[1024];
-	int i, fd, nresp;
+	char *resp;
+	int i, nresp;
 
 	if((ty = jsonbyname(j, "type")) == nil)
 		return -1;
@@ -453,16 +507,10 @@
 		return -1;
 	if(ty->t != JSONString || url->t != JSONString || tok->t != JSONString)
 		return -1;
-	if(strcmp(ty->s, "http-01") != 0)
-		return -1;
-	*matched = 1;
 
-	snprint(path, sizeof(path), "%s/%s", challengedir, tok->s);
-	if((fd = create(path, OWRITE, 0666)) == -1)
-		sysfatal("create: %r"); //return -1;
-	if(fprint(fd, "%s.%s\n", tok->s, jwsthumb) == -1)
+	dprint("trying challenge %s\n", ty->s);
+	if(challengefn(ty->s, tok->s, matched) == -1)
 		return -1;
-	close(fd);
 
 	if((resp = jwsrequest(url->s, &nresp, nil, "{}")) == nil)
 		sysfatal("challenge: post %s: %r", url->s);
@@ -495,18 +543,6 @@
 }
 
 static int
-dnschallenge(JSON*, char*, int*)
-{
-	return -1;
-}
-
-static int
-cmdchallenge(JSON*, char*, int*)
-{
-	return -1;
-}
-
-static int
 dochallenges(JSON *order)
 {
 	JSON *chals, *j, *cl;
@@ -531,7 +567,7 @@
 		}
 		matched = 0;
 		for(ce = cl->first; ce != nil; ce = ce->next){
-			if(challengefn(ce->val, ae->val->s, &matched) == 0)
+			if(challenge(ce->val, ae->val->s, &matched) == 0)
 				break;
 			if(matched)
 				sysfatal("could not complete challenge: %r");
@@ -708,7 +744,7 @@
 static void
 usage(void)
 {
-	fprint(2, "usage: %s [-a acctkey] [-c cmd] [-p provider] [-t chal] [-w chaldir]  acct csr\n", argv0);
+	fprint(2, "usage: %s [-a acctkey][-o chalout] [-p provider] [-t type] acct csr\n", argv0);
 	exits("usage");
 }
 
@@ -715,39 +751,29 @@
 void
 main(int argc, char **argv)
 {
-	char *acctkey, *t;
+	char *acctkey, *ct, *co;
 
 	JSONfmtinstall();
 	fmtinstall('E', Econv);
 
+	ct = "http";
+	co = nil;
 	acctkey = nil;
-	challengefn = httpchallenge;
 	ARGBEGIN{
 	case 'd':
 		debug++;
 		break;
-	case 'p':
-		provider = EARGF(usage());
-		break;
 	case 'a':
 		acctkey = EARGF(usage());
 		break;
-	case 'c':
-		challengecmd = EARGF(usage());
+	case 'o':
+		co = EARGF(usage());
 		break;
-	case 'w':
-		challengedir = EARGF(usage());
+	case 'p':
+		provider = EARGF(usage());
 		break;
 	case 't':
-		t = EARGF(usage());
-		if(strcmp(t, "http") == 0)
-			challengefn = httpchallenge;
-		else if(strcmp(t, "dns") != 0)
-			challengefn = dnschallenge;
-		else if(strcmp(t, "cmd"))
-			challengefn = cmdchallenge;
-		else
-			sysfatal("unknown challenge type '%s' (need http or dns)", t);
+		ct = EARGF(usage());
 		break;
 	default:
 		usage();
@@ -754,7 +780,19 @@
 		break;
 	}ARGEND;
 
-	if(argc != 2)
+	if(strcmp(ct, "http") == 0){
+		challengeout = (co != nil) ? co : "/usr/web/.well-known/acme-challenge";
+		challengefn = httpchallenge;
+	}else if(strcmp(ct, "dns") == 0){
+		challengeout = (co != nil) ? co : "/lib/ndb/dnschallenge";
+		challengefn = dnschallenge;
+	}else{
+		sysfatal("unknown challenge type '%s'", ct);
+	}
+
+	if(argc == 3)
+		challengedom = argv[2];
+	else if(argc != 2)
 		usage();
 
 	if(acctkey == nil)
--- a/acmed.man
+++ b/acmed.man
@@ -4,55 +4,39 @@
 .SH SYNOPSIS
 .B acmed
 [
-.B -o
-.I outdir
+.B -a
+.I acctkey
 ]
 [
-.B -p
-.I provider
+.B -d
+.I domain
 ]
 [
-.B -a
-.I acctkey
+.B -o
+.I chalout
 ]
 [
-[
-.B e
-.I chalcmd
+.B -p
+.I provider
 ]
 [
-.B w
-.I chaldir
+.B -t
+.I type
 ]
 .I acctname
 .I csr
+[
+.I domain
+]
 .SH DESCRIPTION
 Acmed fetches and renews TLS certificates
 using the
-.I acme
+.I acme (RFC8555)
 protocol.
 It requires a pregenerated account key
 and certificate signing key.
 .PP
 There are a number of options.
-.TP
-.B -o
-.I outdir
-Specifies that the signed certificate is placed in
-.I outdir
-in place of the default
-.IR /sys/lib/tls/acme/ .
-.TP
-.B -p
-.I provider
-Specifies that
-.I provider
-is used as the provider URL, in place of the default
-.IR https://acme-v02.api.letsencrypt.org/directory .
-This must be the directory URL for the desired
-.I RFC8555
-compliant provider
-.TP
 .B -a
 .I acctkey
 Specifies that
@@ -65,24 +49,56 @@
 .I jwk
 formatted RSA key.
 .TP
-.B c
-.I csrkey
+.B -d
+specifies the domain name that will be used
+for
+.I DNS
+challenges.
+.TP
+.B -o
+.I chalout
+specifies that the challenge material is
+placed in the location
+.IR chalout .
+.IP
+For HTTP challenges,
+.I chalout
+must be a directory that your choice of
+.I httpd
+will serve at
+.IR http://domain.com/.well-known/acme-challenge .
+For DNS challenges,
+.I chalout
+is a file that should be included in your
+.I ndb
+database.
+.IP
+If unspecified,
+.I http
+challenges will output to
+.IR /usr/web/.well-known/acme-challenge ,
+whle
+.I dns
+challenges will output to
+.IR /lib/ndb/dnschallenge .
+.TP
+.B -p
+.I provider
 Specifies that
-.I csrkey
-is used to produce the CSR sent to
 .I provider
-in place of the default
-.IR /sys/lib/tls/acme/$domain.key .
-The key must be a plan 9 formatted
-RSA key suitable for
-.IR aux/rsa2csr .
+is used as the provider URL, in place of the default
+.IR https://acme-v02.api.letsencrypt.org/directory .
+This must be the directory URL for the desired
+.I RFC8555
+compliant provider
 .TP
-.B w
-.I chaldir
-Specifies that the challenge is written out to
-.IR chaldir .
-For HTTP challenges, this defaults to
-.IR /usr/web/.well-known/acme-challenge/ .
+.B -t
+.I type
+Specifies that the challenge type. Supported challenge
+types are currently
+.I http
+and
+.IR dns .
 .SH EXAMPLES
 Before
 .B acmed