Hi tech@,

The following patch expands acme-client config file `domain` blocks to
allow for a `owner user:group` directive, which allows to get rid of
customs scripts that "fix" permissions for issued certs, mostly needed
in ports land. I don't find it too invasive, so I thought it could be
merged. Most of the code and manpage bits were taken from vmd.

Noteworthy details:

1. fileproc.c pledge is expanded with chown. In particular, I don't
   understand pledge manpage: I was under the impression that wpath and
   fattr would allow for fchown, but I got an "Operation not supported"
   while testing. I guess this is related to the paragraph stating that
   some syscalls be allowed with limitations under some categories.
2. if the private key already exists, keyproc.c won't interfere with its
   ownership, the same way it does now. The fchown call can be moved if
   "fixing" the ownership is desirable.

bye,
-Lucas

diff /usr/src
commit - 93aad84f8cf14cfaff5b9cdb67494e561810ddc4
path + /usr/src
blob - eb5f19eb298c117c3957faa0ed6ced14972ffaca
file + usr.sbin/acme-client/acme-client.conf.5
--- usr.sbin/acme-client/acme-client.conf.5
+++ usr.sbin/acme-client/acme-client.conf.5
@@ -197,6 +197,17 @@ will be used.
 If it is not specified, a default of
 .Pa /var/www/acme
 will be used.
+.It Ic owner Ar user : Ns Ar group
+Set the owner of the key and certificate files create to
+.Ar user
+and
+.Ar group .
+If only
+.Ar user
+is given, only the user is set.
+If only
+.Pf : Ar group
+is given, only the group is set.
 .El
 .Sh FILES
 .Bl -tag -width /etc/examples/acme-client.conf -compact
blob - 4b43b6ef4ac302706859bf14b681cf8052aa55c8
file + usr.sbin/acme-client/extern.h
--- usr.sbin/acme-client/extern.h
+++ usr.sbin/acme-client/extern.h
@@ -207,9 +207,9 @@ int          fileproc(int, const char *, const char *, 
const 
 int             revokeproc(int, const char *, int, int, const char *const *,
                        size_t);
 int             fileproc(int, const char *, const char *, const char *,
-                       const char *);
+                       const char *, uid_t, gid_t);
 int             keyproc(int, const char *, const char **, size_t,
-                       enum keytype);
+                       enum keytype, uid_t, gid_t);
 int             netproc(int, int, int, int, int, int, int,
                        struct authority_c *, const char *const *,
                        size_t);
blob - b8b8651c31907c6d37147c779f302481a1cb3c86
file + usr.sbin/acme-client/fileproc.c
--- usr.sbin/acme-client/fileproc.c
+++ usr.sbin/acme-client/fileproc.c
@@ -29,7 +29,8 @@ serialise(const char *real, const char *v, size_t vsz,
 #include "extern.h"
 
 static int
-serialise(const char *real, const char *v, size_t vsz, const char *v2, size_t 
v2sz)
+serialise(const char *real, const char *v, size_t vsz, const char *v2,
+    size_t v2sz, uid_t uid, gid_t gid)
 {
        int       fd;
        char     *tmp;
@@ -64,6 +65,10 @@ serialise(const char *real, const char *v, size_t vsz,
                warn("fchmod");
                goto out;
        }
+       if (fchown(fd, uid, gid) == -1) {
+               warn("fchown");
+               goto out;
+       }
        if ((ssize_t)vsz != write(fd, v, vsz)) {
                warnx("write");
                goto out;
@@ -91,7 +96,7 @@ fileproc(int certsock, const char *certdir, const char
 
 int
 fileproc(int certsock, const char *certdir, const char *certfile, const char
-    *chainfile, const char *fullchainfile)
+    *chainfile, const char *fullchainfile, uid_t uid, gid_t gid)
 {
        char            *csr = NULL, *ch = NULL;
        size_t           chsz, csz;
@@ -108,7 +113,7 @@ fileproc(int certsock, const char *certdir, const char
         * rpath and cpath for rename, wpath and cpath for
         * writing to the temporary. fattr for fchmod.
         */
-       if (pledge("stdio cpath wpath rpath fattr", NULL) == -1) {
+       if (pledge("stdio cpath wpath rpath fattr chown", NULL) == -1) {
                warn("pledge");
                goto out;
        }
@@ -173,7 +178,7 @@ fileproc(int certsock, const char *certdir, const char
                goto out;
 
        if (chainfile) {
-               if (!serialise(chainfile, ch, chsz, NULL, 0))
+               if (!serialise(chainfile, ch, chsz, NULL, 0, uid, gid))
                        goto out;
 
                dodbg("%s: created", chainfile);
@@ -190,7 +195,7 @@ fileproc(int certsock, const char *certdir, const char
                goto out;
 
        if (certfile) {
-               if (!serialise(certfile, csr, csz, NULL, 0))
+               if (!serialise(certfile, csr, csz, NULL, 0, uid, gid))
                        goto out;
 
                dodbg("%s: created", certfile);
@@ -204,7 +209,7 @@ fileproc(int certsock, const char *certdir, const char
         */
        if (fullchainfile) {
                if (!serialise(fullchainfile, csr, csz, ch,
-                   chsz))
+                   chsz, uid, gid))
                        goto out;
 
                dodbg("%s: created", fullchainfile);
blob - a3b6666c2796c30b97c5628e5f3d350a765c87cb
file + usr.sbin/acme-client/keyproc.c
--- usr.sbin/acme-client/keyproc.c
+++ usr.sbin/acme-client/keyproc.c
@@ -75,7 +75,7 @@ keyproc(int netsock, const char *keyfile, const char *
  */
 int
 keyproc(int netsock, const char *keyfile, const char **alts, size_t altsz,
-    enum keytype keytype)
+    enum keytype keytype, uid_t uid, gid_t gid)
 {
        char            *der64 = NULL, *der = NULL, *dercp;
        char            *sans = NULL, *san = NULL;
@@ -97,7 +97,18 @@ keyproc(int netsock, const char *keyfile, const char *
 
        prev = umask((S_IWUSR | S_IXUSR) | S_IRWXG | S_IRWXO);
        if ((f = fopen(keyfile, "r")) == NULL && errno == ENOENT) {
+               int fd;
+
                f = fopen(keyfile, "wx");
+               if (f != NULL) {
+                       fd = fileno(f);
+                       if (fd >= 0) {
+                               if (fchown(fd, uid, gid) == -1) {
+                                       warn("chown %s", keyfile);
+                                       goto out;
+                               }
+                       }
+               }
                newkey = 1;
        }
        umask(prev);
blob - bec17254297fb1e770411c1cb8ddb718e150ee0f
file + usr.sbin/acme-client/main.c
--- usr.sbin/acme-client/main.c
+++ usr.sbin/acme-client/main.c
@@ -248,7 +248,7 @@ main(int argc, char *argv[])
                close(file_fds[1]);
                c = keyproc(key_fds[0], domain->key,
                    (const char **)alts, altsz,
-                   domain->keytype);
+                   domain->keytype, domain->owner.uid, domain->owner.gid);
                exit(c ? EXIT_SUCCESS : EXIT_FAILURE);
        }
 
@@ -319,7 +319,7 @@ main(int argc, char *argv[])
                close(dns_fds[0]);
                close(rvk_fds[0]);
                c = fileproc(file_fds[1], certdir, domain->cert, domain->chain,
-                   domain->fullchain);
+                   domain->fullchain, domain->owner.uid, domain->owner.gid);
                /*
                 * This is different from the other processes in that it
                 * can return 2 if the certificates were updated.
blob - 3954f62a0d0894d8fd243944e8393c2e9dc2e70e
file + usr.sbin/acme-client/parse.h
--- usr.sbin/acme-client/parse.h
+++ usr.sbin/acme-client/parse.h
@@ -17,6 +17,7 @@
 #ifndef PARSE_H
 #define PARSE_H
 
+#include <sys/types.h> /* XXX */
 #include <sys/queue.h>
 
 #define AUTH_MAXLEN    120     /* max length of an authority_c name */
@@ -54,6 +55,10 @@ struct domain_c {
        char                    *fullchain;
        char                    *auth;
        char                    *challengedir;
+       struct {
+               uid_t            uid;
+               gid_t            gid;
+       }                        owner;
 };
 
 struct altname_c {
blob - 2b0d55f20b1947c94defc1c3b07d790e8bea56c8
file + usr.sbin/acme-client/parse.y
--- usr.sbin/acme-client/parse.y
+++ usr.sbin/acme-client/parse.y
@@ -30,7 +30,9 @@
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
+#include <grp.h>
 #include <limits.h>
+#include <pwd.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -94,6 +96,10 @@ typedef struct {
        union {
                int64_t          number;
                char            *string;
+               struct {
+                       uid_t    uid;
+                       gid_t    gid;
+               }                owner;
        } v;
        int lineno;
 } YYSTYPE;
@@ -102,6 +108,7 @@ typedef struct {
 
 %token AUTHORITY URL API ACCOUNT CONTACT
 %token DOMAIN ALTERNATIVE NAME NAMES CERT FULL CHAIN KEY SIGN WITH CHALLENGEDIR
+%token OWNER
 %token YES NO
 %token INCLUDE
 %token ERROR
@@ -110,6 +117,7 @@ typedef struct {
 %token <v.number>      NUMBER
 %type  <v.string>      string
 %type  <v.number>      keytype
+%type  <v.owner>       owner_id
 
 %%
 
@@ -393,6 +401,10 @@ domainoptsl        : ALTERNATIVE NAMES '{' optnl altname_l 
'}
                                err(EXIT_FAILURE, "strdup");
                        domain->challengedir = s;
                }
+               | OWNER owner_id {
+                       domain->owner.uid = $2.uid;
+                       domain->owner.gid = $2.gid;
+               }
                ;
 
 altname_l      : altname optcommanl altname_l
@@ -421,6 +433,48 @@ altname            : STRING {
                        */
                }
 
+owner_id       : NUMBER {
+                       $$.uid = $1;
+                       $$.gid = 0;
+               }
+               | STRING {
+                       char            *user, *group;
+                       struct passwd   *pw;
+                       struct group    *gr;
+
+                       $$.uid = $$.gid = 0;
+
+                       user = $1;
+                       if ((group = strchr(user, ':')) != NULL) {
+                               if (group == user)
+                                       user = NULL;
+                               *group++ = '\0';
+                       }
+
+                       if (user != NULL && *user) {
+                               if ((pw = getpwnam(user)) == NULL) {
+                                       yyerror("failed to get user: %s",
+                                           user);
+                                       free($1);
+                                       YYERROR;
+                               }
+                               $$.uid = pw->pw_uid;
+                       }
+
+                       if (group != NULL && *group) {
+                               if ((gr = getgrnam(group)) == NULL) {
+                                       yyerror("failed to get group: %s",
+                                           group);
+                                       free($1);
+                                       YYERROR;
+                               }
+                               $$.gid = gr->gr_gid;
+                       }
+
+                       free($1);
+               }
+               ;
+
 %%
 
 struct keywords {
@@ -470,6 +524,7 @@ lookup(char *s)
                {"key",                 KEY},
                {"name",                NAME},
                {"names",               NAMES},
+               {"owner",               OWNER},
                {"rsa",                 RSA},
                {"sign",                SIGN},
                {"url",                 URL},

Reply via email to