akaros/kern/drivers/dev/capability.c
<<
>>
Prefs
   1/*
   2 * This file is part of the UCB release of Plan 9. It is subject to the license
   3 * terms in the LICENSE file found in the top-level directory of this
   4 * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
   5 * part of the UCB release of Plan 9, including this file, may be copied,
   6 * modified, propagated, or distributed except according to the terms contained
   7 * in the LICENSE file.
   8 */
   9
  10#include <slab.h>
  11#include <kmalloc.h>
  12#include <kref.h>
  13#include <string.h>
  14#include <stdio.h>
  15#include <assert.h>
  16#include <error.h>
  17#include <cpio.h>
  18#include <pmap.h>
  19#include <smp.h>
  20#include <net/ip.h>
  21
  22#include <crypto/2crypto.h>
  23#include <crypto/2hmac.h>
  24#include <crypto/2id.h>
  25#include <crypto/2sha.h>
  26
  27#include <ctype.h>
  28
  29enum {
  30        Hashlen = VB2_MAX_DIGEST_SIZE * 2,
  31        Maxhash = 256,
  32};
  33
  34/*
  35 *  if a process knows cap->cap, it can change user
  36 *  to capabilty->user.
  37 */
  38struct Caphash {
  39        struct Caphash *next;
  40        char hash[Hashlen + 1];
  41};
  42
  43struct {
  44        qlock_t qlock;
  45        struct Caphash *first;
  46        int nhash;
  47} capalloc;
  48
  49enum {
  50        Qdir,
  51        Qhash,
  52        Quse,
  53};
  54
  55/* caphash must be last */
  56struct dirtab capdir[] = {
  57        {".",       {Qdir, 0, QTDIR}, 0, DMDIR | 0500},
  58        {"capuse",  {Quse},           0, 0222,},
  59        {"caphash", {Qhash},          0, 0200,},
  60};
  61int ncapdir = ARRAY_SIZE(capdir);
  62
  63static void capinit(void)
  64{
  65        qlock_init(&capalloc.qlock);
  66}
  67
  68static struct chan *capattach(char *spec)
  69{
  70        return devattach("capability", spec);
  71}
  72
  73static struct walkqid *capwalk(struct chan *c, struct chan *nc, char **name,
  74                               unsigned int nname)
  75{
  76        return devwalk(c, nc, name, nname, capdir, ncapdir, devgen);
  77}
  78
  79static void capremove(struct chan *c)
  80{
  81        if (iseve() && c->qid.path == Qhash)
  82                ncapdir = ARRAY_SIZE(capdir) - 1;
  83        else
  84                error(EPERM, "Permission denied");
  85}
  86
  87static size_t capstat(struct chan *c, uint8_t *db, size_t n)
  88{
  89        return devstat(c, db, n, capdir, ncapdir, devgen);
  90}
  91
  92/*
  93 *  if the stream doesn't exist, create it
  94 */
  95static struct chan *capopen(struct chan *c, int omode)
  96{
  97        if (c->qid.type & QTDIR) {
  98                if (openmode(omode) != O_READ)
  99                        error(EISDIR, "Can only read a directory");
 100                c->mode = openmode(omode);
 101                c->flag |= COPEN;
 102                c->offset = 0;
 103                return c;
 104        }
 105
 106        switch ((uint32_t)c->qid.path) {
 107        case Qhash:
 108                if (!iseve())
 109                        error(EPERM,
 110                              "Permission denied: only eve can open Qhash");
 111                break;
 112        }
 113
 114        c->mode = openmode(omode);
 115        c->flag |= COPEN;
 116        c->offset = 0;
 117        return c;
 118}
 119
 120static size_t __hashstr(char *buf, uint8_t *hash, size_t bytes_to_split)
 121{
 122        int i;
 123
 124        for (i = 0; i < bytes_to_split; i++)
 125                snprintf(buf + 2 * i, 3, "%02x", hash[i]);
 126
 127        return bytes_to_split;
 128}
 129
 130static struct Caphash *remcap(uint8_t *hash)
 131{
 132        struct Caphash *t, **l;
 133
 134        qlock(&capalloc.qlock);
 135
 136        /* find the matching capability */
 137        for (l = &capalloc.first; *l != NULL;) {
 138                t = *l;
 139                if (memcmp(hash, t->hash, Hashlen) == 0)
 140                        break;
 141                l = &t->next;
 142        }
 143        t = *l;
 144        if (t != NULL) {
 145                capalloc.nhash--;
 146                *l = t->next;
 147        }
 148        qunlock(&capalloc.qlock);
 149
 150        return t;
 151}
 152
 153/* add a capability, throwing out any old ones */
 154static void addcap(uint8_t *hash)
 155{
 156        struct Caphash *p, *t, **l;
 157
 158        p = kzmalloc(sizeof(*p), MEM_WAIT);
 159        memmove(p->hash, hash, Hashlen);
 160        p->next = NULL;
 161
 162        qlock(&capalloc.qlock);
 163
 164        /* trim extras */
 165        while (capalloc.nhash >= Maxhash) {
 166                t = capalloc.first;
 167                if (t == NULL)
 168                        panic("addcap");
 169                capalloc.first = t->next;
 170                kfree(t);
 171                capalloc.nhash--;
 172        }
 173
 174        /* add new one */
 175        for (l = &capalloc.first; *l != NULL; l = &(*l)->next)
 176                ;
 177        *l = p;
 178        capalloc.nhash++;
 179
 180        qunlock(&capalloc.qlock);
 181}
 182
 183static void capclose(struct chan *c)
 184{
 185}
 186
 187static size_t capread(struct chan *c, void *va, size_t n, off64_t m)
 188{
 189        switch ((uint32_t)c->qid.path) {
 190        case Qdir:
 191                return devdirread(c, va, n, capdir, ncapdir, devgen);
 192
 193        default:
 194                error(EPERM, "Permission denied: can't read capability files");
 195                break;
 196        }
 197        return n;
 198}
 199
 200static size_t capwrite(struct chan *c, void *va, size_t n, off64_t m)
 201{
 202        struct Caphash *p;
 203        char *cp, *hashstr;
 204        uint8_t hash[Hashlen + 1] = {0};
 205        char *key, *from, *to;
 206        int ret;
 207        ERRSTACK(1);
 208
 209        switch ((uint32_t)c->qid.path) {
 210        case Qhash:
 211                if (!iseve())
 212                        error(EPERM, "permission denied: you must be eve");
 213                if (n < VB2_SHA256_DIGEST_SIZE * 2)
 214                        error(EIO, "Short read: on Qhash");
 215                memmove(hash, va, Hashlen);
 216                for (int i = 0; i < Hashlen; i++)
 217                        hash[i] = tolower(hash[i]);
 218                addcap(hash);
 219                break;
 220
 221        case Quse:
 222                /* copy key to avoid a fault in hmac_xx and so we can enforce
 223                 * null termination. */
 224                cp = kzmalloc(n + 1, MEM_WAIT);
 225                hashstr = kzmalloc(sizeof(hash), MEM_WAIT);
 226                if (waserror()) {
 227                        kfree(cp);
 228                        kfree(hashstr);
 229                        nexterror();
 230                }
 231                memmove(cp, va, n);
 232                cp[n] = 0;
 233
 234                from = cp;
 235                key = strrchr(cp, '@');
 236                if (key == NULL)
 237                        error(EIO, "short read: Quse");
 238                *key++ = 0;
 239
 240                ret = hmac(VB2_HASH_SHA256, key, strlen(key),
 241                           from, strlen(from), hash, Hashlen);
 242                if (ret)
 243                        error(EINVAL, "HMAC failed");
 244
 245                // Convert to ASCII text
 246                ret = __hashstr(hashstr, hash, VB2_SHA256_DIGEST_SIZE);
 247                if (ret != VB2_SHA256_DIGEST_SIZE)
 248                        error(EINVAL, "hash is wrong length");
 249
 250                p = remcap((uint8_t *)hashstr);
 251                if (p == NULL)
 252                        error(EINVAL, "invalid capability %s@%s", from, key);
 253                kfree(p);
 254
 255                /* if a from user is supplied, make sure it matches */
 256                to = strchr(from, '@');
 257                if (to == NULL) {
 258                        to = from;
 259                } else {
 260                        *to++ = 0;
 261                        if (strcmp(from, current->user.name) != 0)
 262                                error(EINVAL, "capability must match user");
 263                }
 264
 265                /* set user id */
 266                // In the original user names were NULL-terminated; ensure
 267                // that is still the case.
 268                if (strlen(to) > sizeof(current->user.name) - 1)
 269                        error(EINVAL, "New user name is > %d bytes",
 270                              sizeof(current->user.name) - 1);
 271                proc_set_username(current, to);
 272                //up->basepri = PriNormal;
 273
 274                kfree(cp);
 275                kfree(hashstr);
 276                poperror();
 277                break;
 278
 279        default:
 280                error(EPERM, "permission denied: capwrite");
 281                break;
 282        }
 283
 284        return n;
 285}
 286
 287struct dev capdevtab __devtab = {
 288        .name = "capability",
 289
 290        .reset = devreset,
 291        .init = capinit,
 292        .shutdown = devshutdown,
 293        .attach = capattach,
 294        .walk = capwalk,
 295        .stat = capstat,
 296        .open = capopen,
 297        .create = devcreate,
 298        .close = capclose,
 299        .read = capread,
 300        .bread = devbread,
 301        .write = capwrite,
 302        .bwrite = devbwrite,
 303        .remove = capremove,
 304        .wstat = devwstat,
 305};
 306