Whamcloud - gitweb
- make HEAD from b_post_cmd3
[fs/lustre-release.git] / lustre / obdfilter / filter_capa.c
diff --git a/lustre/obdfilter/filter_capa.c b/lustre/obdfilter/filter_capa.c
new file mode 100644 (file)
index 0000000..83ccbed
--- /dev/null
@@ -0,0 +1,303 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2005 Cluster File Systems, Inc.
+ *
+ * Author: Lai Siyao <lsy@clusterfs.com>
+ *
+ *   This file is part of Lustre, http://www.lustre.org.
+ *
+ *   Lustre is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2 of the GNU General Public
+ *   License as published by the Free Software Foundation.
+ *
+ *   Lustre is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with Lustre; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define DEBUG_SUBSYSTEM S_FILTER
+
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+#include <linux/file.h>
+#include <linux/kmod.h>
+
+#include <lustre_fsfilt.h>
+#include <lustre_capa.h>
+
+#include "filter_internal.h"
+
+/*
+ * FIXME
+ * keep this as simple as possible. we suppose the blacklist usually
+ * be empry or very short (<5), since long term blacklist should be
+ * done on MDS side. A more sophisticated blacklist will be implemented
+ * later.
+ *
+ * note blacklist didn't take effect when OSS capability disabled. this
+ * looks reasonable to me.
+ */
+#define BLACKLIST_MAX   (32)
+static int nblacklist = 0;
+static uid_t blacklist[BLACKLIST_MAX];
+static spinlock_t blacklist_lock = SPIN_LOCK_UNLOCKED;
+
+int blacklist_display(char *buf, int bufsize)
+{
+        char one[16];
+        int i;
+        LASSERT(buf);
+
+        buf[0] = '\0';
+        spin_lock(&blacklist_lock);
+        for (i = 0; i < nblacklist; i++) {
+                snprintf(one, 16, "%u\n", blacklist[i]);
+                strncat(buf, one, bufsize);
+        }
+        spin_unlock(&blacklist_lock);
+        return strnlen(buf, bufsize);
+}
+
+void blacklist_add(uid_t uid)
+{
+        int i;
+
+        spin_lock(&blacklist_lock);
+        if (nblacklist == BLACKLIST_MAX) {
+                CERROR("can't add more in blacklist\n");
+                spin_unlock(&blacklist_lock);
+                return;
+        }
+
+        for (i = 0; i < nblacklist; i++) {
+                if (blacklist[i] == uid) {
+                        spin_unlock(&blacklist_lock);
+                        return;
+                }
+        }
+
+        blacklist[nblacklist++] = uid;
+        spin_unlock(&blacklist_lock);
+}
+
+void blacklist_del(uid_t uid)
+{
+        int i;
+
+        spin_lock(&blacklist_lock);
+        for (i = 0; i < nblacklist; i++) {
+                if (blacklist[i] == uid) {
+                        nblacklist--;
+                        while (i < nblacklist) {
+                                blacklist[i] = blacklist[i+1];
+                                i++;
+                        }
+                        spin_unlock(&blacklist_lock);
+                        return;
+                }
+        }
+        spin_unlock(&blacklist_lock);
+}
+
+static int blacklist_check(uid_t uid)
+{
+        int i, rc = 0;
+
+        spin_lock(&blacklist_lock);
+        for (i = 0; i < nblacklist; i++) {
+                if (blacklist[i] == uid) {
+                        rc = 1;
+                        break;
+                }
+        }
+        spin_unlock(&blacklist_lock);
+        return rc;
+}
+
+static inline __u32 filter_ck_keyid(struct filter_capa_key *key)
+{
+        return key->k_key.lk_keyid;
+}
+
+int filter_update_capa_key(struct obd_device *obd, struct lustre_capa_key *new)
+{
+        struct filter_obd *filter = &obd->u.filter;
+        struct filter_capa_key *k, *keys[2] = { NULL, NULL };
+        int i;
+
+        spin_lock(&capa_lock);
+        list_for_each_entry(k, &filter->fo_capa_keys, k_list) {
+                if (k->k_key.lk_mdsid != new->lk_mdsid)
+                        continue;
+
+                if (keys[0]) {
+                        keys[1] = k;
+                        if (filter_ck_keyid(keys[1]) > filter_ck_keyid(keys[0]))
+                                keys[1] = keys[0], keys[0] = k;
+                } else {
+                        keys[0] = k;
+                }
+        }
+        spin_unlock(&capa_lock);
+
+        for (i = 0; i < 2; i++) {
+                if (!keys[i])
+                        continue;
+                if (filter_ck_keyid(keys[i]) != new->lk_keyid)
+                        continue;
+                /* maybe because of recovery or other reasons, MDS sent the
+                 * the old capability key again.
+                 */
+                spin_lock(&capa_lock);
+                keys[i]->k_key = *new;
+                spin_unlock(&capa_lock);
+
+                RETURN(0);
+        }
+
+        if (keys[1]) {
+                /* if OSS already have two keys, update the old one */
+                k = keys[1];
+        } else {
+                OBD_ALLOC_PTR(k);
+                if (!k)
+                        RETURN(-ENOMEM);
+                INIT_LIST_HEAD(&k->k_list);
+        }
+
+        spin_lock(&capa_lock);
+        k->k_key = *new;
+        if (list_empty(&k->k_list))
+                list_add(&k->k_list, &filter->fo_capa_keys);
+        spin_unlock(&capa_lock);
+
+        DEBUG_CAPA_KEY(D_SEC, new, "new");
+        RETURN(0);
+}
+
+int filter_auth_capa(struct obd_export *exp, struct lu_fid *fid, __u64 mdsid,
+                     struct lustre_capa *capa, __u64 opc)
+{
+        struct obd_device *obd = exp->exp_obd;
+        struct filter_obd *filter = &obd->u.filter;
+        struct filter_capa_key *k;
+        struct lustre_capa_key key;
+        struct obd_capa *oc;
+        __u8 *hmac;
+        int keys_ready = 0, key_found = 0, rc = 0;
+        ENTRY;
+
+        /* capability is disabled */
+        if (!filter->fo_fl_oss_capa)
+                RETURN(0);
+
+        if (capa == NULL) {
+                if (fid)
+                        CERROR("mdsno/fid/opc "LPU64"/"DFID"/"LPX64
+                               ": no capability has been passed\n",
+                               mdsid, PFID(fid), opc);
+                else
+                        CERROR("mdsno/opc "LPU64"/"LPX64
+                               ": no capability has been passed\n",
+                               mdsid, opc);
+                RETURN(-EACCES);
+        }
+
+        if (blacklist_check(capa->lc_uid)) {
+                DEBUG_CAPA(D_ERROR, capa, "uid %u found in blacklist,",
+                           capa->lc_uid);
+                RETURN(-EACCES);
+        }
+
+#warning "enable fid check in filter_auth_capa() when fid stored in OSS object"
+
+        if (opc == CAPA_OPC_OSS_READ) {
+                if (!(capa->lc_opc & CAPA_OPC_OSS_RW))
+                        rc = -EACCES;
+        } else if (!capa_opc_supported(capa, opc)) {
+                rc = -EACCES;
+        }
+        if (rc) {
+                DEBUG_CAPA(D_ERROR, capa, "opc "LPX64" not supported by", opc);
+                RETURN(rc);
+        }
+
+        oc = capa_lookup(filter->fo_capa_hash, capa, 0);
+        if (oc) {
+                spin_lock(&oc->c_lock);
+                if (capa_is_expired(oc)) {
+                        DEBUG_CAPA(D_ERROR, capa, "expired");
+                        rc = -ESTALE;
+                }
+                spin_unlock(&oc->c_lock);
+
+                capa_put(oc);
+                RETURN(rc);
+        }
+
+        spin_lock(&capa_lock);
+        list_for_each_entry(k, &filter->fo_capa_keys, k_list)
+                if (k->k_key.lk_mdsid == mdsid) {
+                        keys_ready = 1;
+                        if (k->k_key.lk_keyid == capa_keyid(capa)) {
+                                key = k->k_key;
+                                key_found = 1;
+                                break;
+                        }
+                }
+        spin_unlock(&capa_lock);
+
+        if (!keys_ready) {
+                CDEBUG(D_SEC, "MDS hasn't propagated capability keys yet, "
+                       "ignore check!\n");
+                RETURN(0);
+        }
+
+       if (!key_found) {
+                DEBUG_CAPA(D_ERROR, capa, "no matched capability key for");
+                RETURN(-ESTALE);
+        }
+
+        OBD_ALLOC(hmac, CAPA_HMAC_MAX_LEN);
+        if (hmac == NULL)
+                RETURN(-ENOMEM);
+
+        rc = capa_hmac(hmac, capa, key.lk_key);
+        if (rc) {
+                DEBUG_CAPA(D_ERROR, capa, "HMAC failed: rc %d", rc);
+                OBD_FREE(hmac, CAPA_HMAC_MAX_LEN);
+                RETURN(rc);
+        }
+
+        rc = memcmp(hmac, capa->lc_hmac, CAPA_HMAC_MAX_LEN);
+        OBD_FREE(hmac, CAPA_HMAC_MAX_LEN);
+        if (rc) {
+                DEBUG_CAPA_KEY(D_ERROR, &key, "calculate HMAC with ");
+                DEBUG_CAPA(D_ERROR, capa, "HMAC mismatch");
+                RETURN(-EACCES);
+        }
+
+        /* store in capa hash */
+        oc = capa_add(filter->fo_capa_hash, capa);
+        capa_put(oc);
+        RETURN(0);
+}
+
+void filter_free_capa_keys(struct filter_obd *filter)
+{
+        struct filter_capa_key *key, *n;
+
+        spin_lock(&capa_lock);
+        list_for_each_entry_safe(key, n, &filter->fo_capa_keys, k_list) {
+                list_del_init(&key->k_list);
+                OBD_FREE(key, sizeof(*key));
+        }
+        spin_unlock(&capa_lock);
+}