cap: fix format-string vulnerability
[akaros.git] / kern / drivers / dev / capability.c
index 39013aa..f489e6f 100644 (file)
@@ -7,8 +7,6 @@
  * in the LICENSE file.
  */
 
-#include <vfs.h>
-#include <kfs.h>
 #include <slab.h>
 #include <kmalloc.h>
 #include <kref.h>
 #include <cpio.h>
 #include <pmap.h>
 #include <smp.h>
-#include <ip.h>
+#include <net/ip.h>
 
 #include <crypto/2crypto.h>
 #include <crypto/2hmac.h>
 #include <crypto/2id.h>
 #include <crypto/2sha.h>
 
+#include <ctype.h>
+
 enum {
-       Hashlen = VB2_SHA256_BLOCK_SIZE,
+       Hashlen = VB2_MAX_DIGEST_SIZE * 2,
        Maxhash = 256,
 };
 
@@ -37,7 +37,7 @@ enum {
  */
 struct Caphash {
        struct Caphash *next;
-       char hash[Hashlen];
+       char hash[Hashlen + 1];
 };
 
 struct {
@@ -55,18 +55,23 @@ enum {
 /* caphash must be last */
 struct dirtab capdir[] = {
        {".",       {Qdir, 0, QTDIR}, 0, DMDIR | 0500},
-       {"capuse",  {Quse}, 0, 0222,},
+       {"capuse",  {Quse},           0, 0222,},
        {"caphash", {Qhash},          0, 0200,},
 };
 int ncapdir = ARRAY_SIZE(capdir);
 
+static void capinit(void)
+{
+       qlock_init(&capalloc.qlock);
+}
+
 static struct chan *capattach(char *spec)
 {
        return devattach("capability", spec);
 }
 
 static struct walkqid *capwalk(struct chan *c, struct chan *nc, char **name,
-                               int nname)
+                               unsigned int nname)
 {
        return devwalk(c, nc, name, nname, capdir, ncapdir, devgen);
 }
@@ -79,7 +84,7 @@ static void capremove(struct chan *c)
                error(EPERM, "Permission denied");
 }
 
-static int32_t capstat(struct chan *c, uint8_t *db, int32_t n)
+static size_t capstat(struct chan *c, uint8_t *db, size_t n)
 {
        return devstat(c, db, n, capdir, ncapdir, devgen);
 }
@@ -90,9 +95,9 @@ static int32_t capstat(struct chan *c, uint8_t *db, int32_t n)
 static struct chan *capopen(struct chan *c, int omode)
 {
        if (c->qid.type & QTDIR) {
-               if (omode != O_RDONLY)
-                       error(EISDIR, "Is a directory");
-               c->mode = omode;
+               if (openmode(omode) != O_READ)
+                       error(EISDIR, "Can only read a directory");
+               c->mode = openmode(omode);
                c->flag |= COPEN;
                c->offset = 0;
                return c;
@@ -101,7 +106,8 @@ static struct chan *capopen(struct chan *c, int omode)
        switch ((uint32_t)c->qid.path) {
        case Qhash:
                if (!iseve())
-                       error(EPERM, "Permission denied: only eve() can open Qhash");
+                       error(EPERM,
+                             "Permission denied: only eve can open Qhash");
                break;
        }
 
@@ -111,19 +117,15 @@ static struct chan *capopen(struct chan *c, int omode)
        return c;
 }
 
-/*
-  static char*
-  hashstr(uint8_t *hash)
-  {
-  static char buf[2*Hashlen+1];
-  int i;
-
-  for(i = 0; i < Hashlen; i++)
-  sprint(buf+2*i, "%2.2x", hash[i]);
-  buf[2*Hashlen] = 0;
-  return buf;
-  }
-*/
+static size_t __hashstr(char *buf, uint8_t *hash, size_t bytes_to_split)
+{
+       int i;
+
+       for (i = 0; i < bytes_to_split; i++)
+               snprintf(buf + 2 * i, 3, "%02x", hash[i]);
+
+       return bytes_to_split;
+}
 
 static struct Caphash *remcap(uint8_t *hash)
 {
@@ -182,7 +184,7 @@ static void capclose(struct chan *c)
 {
 }
 
-static long capread(struct chan *c, void *va, long n, int64_t m)
+static size_t capread(struct chan *c, void *va, size_t n, off64_t m)
 {
        switch ((uint32_t)c->qid.path) {
        case Qdir:
@@ -195,13 +197,13 @@ static long capread(struct chan *c, void *va, long n, int64_t m)
        return n;
 }
 
-static long capwrite(struct chan *c, void *va, long n, int64_t m)
+static size_t capwrite(struct chan *c, void *va, size_t n, off64_t m)
 {
        struct Caphash *p;
        char *cp;
-       uint8_t hash[Hashlen];
+       uint8_t hash[Hashlen + 1] = {0};
+       char *hashstr = NULL;
        char *key, *from, *to;
-       char err[256];
        int ret;
        ERRSTACK(1);
 
@@ -209,17 +211,21 @@ static long capwrite(struct chan *c, void *va, long n, int64_t m)
        case Qhash:
                if (!iseve())
                        error(EPERM, "permission denied: you must be eve");
-               if (n < Hashlen)
+               if (n < VB2_SHA256_DIGEST_SIZE * 2)
                        error(EIO, "Short read: on Qhash");
                memmove(hash, va, Hashlen);
+               for (int i = 0; i < Hashlen; i++)
+                       hash[i] = tolower(hash[i]);
                addcap(hash);
                break;
 
        case Quse:
-               /* copy key to avoid a fault in hmac_xx */
+               /* copy key to avoid a fault in hmac_xx and so we can enforce
+                * null termination. */
                cp = NULL;
                if (waserror()) {
                        kfree(cp);
+                       kfree(hashstr);
                        nexterror();
                }
                cp = kzmalloc(n + 1, 0);
@@ -232,16 +238,23 @@ static long capwrite(struct chan *c, void *va, long n, int64_t m)
                        error(EIO, "short read: Quse");
                *key++ = 0;
 
-               ret = hmac(VB2_ALG_RSA1024_SHA256, key, strlen(key),
-                          from, strlen(from), hash, sizeof(hash));
+               ret = hmac(VB2_HASH_SHA256, key, strlen(key),
+                          from, strlen(from), hash, Hashlen);
                if (ret)
                        error(EINVAL, "HMAC failed");
 
-               p = remcap(hash);
-               if (p == NULL) {
-                       snprintf(err, sizeof(err), "invalid capability %s@%s", from, key);
-                       error(EINVAL, err);
-               }
+               // Convert to ASCII text
+               hashstr = (char *)kzmalloc(sizeof(hash), MEM_WAIT);
+               ret = __hashstr(hashstr, hash, VB2_SHA256_DIGEST_SIZE);
+               if (ret != VB2_SHA256_DIGEST_SIZE)
+                       error(EINVAL, "hash is wrong length");
+
+               p = remcap((uint8_t *)hashstr);
+               if (p == NULL)
+                       error(EINVAL, "invalid capability %s@%s", from, key);
+
+               kfree(hashstr);
+               hashstr = NULL;
 
                /* if a from user is supplied, make sure it matches */
                to = strchr(from, '@');
@@ -249,25 +262,19 @@ static long capwrite(struct chan *c, void *va, long n, int64_t m)
                        to = from;
                } else {
                        *to++ = 0;
-                       panic("todo");
-                       /*
-                       if (strcmp(from, up->user) != 0)
+                       if (strcmp(from, current->user.name) != 0)
                                error(EINVAL, "capability must match user");
-                       */
                }
 
                /* set user id */
-               // TODO: make user a char *, not a fixed array.
-               //kstrdup(&current->user, to);
                // In the original user names were NULL-terminated; ensure
                // that is still the case.
-               if (strlen(to) > sizeof(current->user)-1)
-                       error(EINVAL, "New user name is > %d bytes", sizeof(current->user));
-               memset(current->user, 0, sizeof(current->user));
-               strncpy(current->user, to, sizeof(current->user));
+               if (strlen(to) > sizeof(current->user.name) - 1)
+                       error(EINVAL, "New user name is > %d bytes",
+                             sizeof(current->user.name) - 1);
+               proc_set_username(current, to);
                //up->basepri = PriNormal;
 
-
                kfree(p);
                kfree(cp);
                poperror();
@@ -285,7 +292,7 @@ struct dev capdevtab __devtab = {
        .name = "capability",
 
        .reset = devreset,
-       .init = devinit,
+       .init = capinit,
        .shutdown = devshutdown,
        .attach = capattach,
        .walk = capwalk,