Whamcloud - gitweb
LU-5478 obdclass: get rid of obd_* typedefs
[fs/lustre-release.git] / lustre / ofd / ofd_capa.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2012, 2014 Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  *
32  * lustre/ofd/ofd_capa.c
33  *
34  * This file provides helper functions for Lustre capability key management and
35  * capability authentication. A capability is a token of authority, which is
36  * distributed by MDT to client upon open/lookup/getattr/setattr and unlink
37  * (this is not needed for new servers because destroying objects on OST is
38  * originated from MDT, which doesn't need capability), and will be packed
39  * into subsequent requests to MDT and OST. Capability key is shared by MDT and
40  * OST, which is used to sign and authenticate capability (HMAC algorithm).
41  *
42  * Author: Lai Siyao <lai.siyao@intel.com>
43  */
44
45 #define DEBUG_SUBSYSTEM S_FILTER
46
47 #include "ofd_internal.h"
48
49 static inline __u32 ofd_ck_keyid(struct filter_capa_key *key)
50 {
51         return key->k_key.lk_keyid;
52 }
53
54 /**
55  * Update capability key
56  *
57  * A new capability key is received from MDT, because MDT only uses the
58  * latest key to sign capability, OFD caches the latest two keys in case client
59  * still helds capability signed with old key.
60  *
61  * \param[in] ofd       OFD device
62  * \param[in] new       new capability key
63  * \retval              0 on success
64  * \retval              negative number on error
65  */
66 int ofd_update_capa_key(struct ofd_device *ofd, struct lustre_capa_key *new)
67 {
68         struct obd_device       *obd = ofd_obd(ofd);
69         struct filter_capa_key  *k, *keys[2] = { NULL, NULL };
70         int                      i;
71
72         spin_lock(&capa_lock);
73         list_for_each_entry(k, &obd->u.filter.fo_capa_keys, k_list) {
74                 if (k->k_key.lk_seq != new->lk_seq)
75                         continue;
76
77                 if (keys[0]) {
78                         keys[1] = k;
79                         if (ofd_ck_keyid(keys[1]) > ofd_ck_keyid(keys[0]))
80                                 keys[1] = keys[0], keys[0] = k;
81                 } else {
82                         keys[0] = k;
83                 }
84         }
85         spin_unlock(&capa_lock);
86
87         for (i = 0; i < 2; i++) {
88                 if (!keys[i])
89                         continue;
90                 if (ofd_ck_keyid(keys[i]) != new->lk_keyid)
91                         continue;
92                 /* maybe because of recovery or other reasons, MDS sent the
93                  * the old capability key again.
94                  */
95                 spin_lock(&capa_lock);
96                 keys[i]->k_key = *new;
97                 spin_unlock(&capa_lock);
98
99                 RETURN(0);
100         }
101
102         if (keys[1]) {
103                 /* if OSS already have two keys, update the old one */
104                 k = keys[1];
105         } else {
106                 OBD_ALLOC_PTR(k);
107                 if (!k)
108                         RETURN(-ENOMEM);
109                 INIT_LIST_HEAD(&k->k_list);
110         }
111
112         spin_lock(&capa_lock);
113         k->k_key = *new;
114         if (list_empty(&k->k_list))
115                 list_add(&k->k_list, &obd->u.filter.fo_capa_keys);
116         spin_unlock(&capa_lock);
117
118         DEBUG_CAPA_KEY(D_SEC, new, "new");
119         RETURN(0);
120 }
121
122 /**
123  * Authenticate capability
124  *
125  * OFD authenticate the capability packed in client request. Firstly, it will
126  * lookup from local cache, if found, compare with it, otherwise sign it with
127  * capability key to validate it. If the capability is valid, it will be added
128  * into local cache for later use.
129  *
130  * \param[in] exp       export for the client
131  * \param[in] fid       master fid (on MDT) of the file
132  * \param[in] seq       OST sequence extracted from master fid
133  * \param[in] capa      capability extracted from client request
134  * \param[in] opc       opcode the caller requested
135  * \retval              0 on success
136  * \retval              negative number on error
137  */
138 int ofd_auth_capa(struct obd_export *exp, const struct lu_fid *fid,
139                   u64 seq, struct lustre_capa *capa, __u64 opc)
140 {
141         struct filter_obd       *filter = &exp->exp_obd->u.filter;
142         struct filter_capa_key  *k;
143         struct lustre_capa_key   key;
144         struct obd_capa         *oc;
145         __u8                    *hmac;
146         int                      keys_ready = 0, key_found = 0, rc = 0;
147
148         ENTRY;
149
150         /* skip capa check for llog and obdecho */
151         if (!fid_seq_is_mdt(seq))
152                 RETURN(0);
153
154         /* capability is disabled */
155         if (!filter->fo_fl_oss_capa)
156                 RETURN(0);
157
158         if (!(exp_connect_flags(exp) & OBD_CONNECT_OSS_CAPA))
159                 RETURN(0);
160
161         if (capa == NULL) {
162                 if (fid)
163                         CERROR("seq/fid/opc "LPU64"/"DFID"/"LPX64
164                                ": no capability has been passed\n",
165                                seq, PFID(fid), opc);
166                 else
167                         CERROR("seq/opc "LPU64"/"LPX64
168                                ": no capability has been passed\n",
169                                seq, opc);
170                 RETURN(-EACCES);
171         }
172
173         if (opc == CAPA_OPC_OSS_READ) {
174                 if (!(capa->lc_opc & CAPA_OPC_OSS_RW))
175                         rc = -EACCES;
176         } else if (!capa_opc_supported(capa, opc)) {
177                 rc = -EACCES;
178         }
179
180         if (rc) {
181                 DEBUG_CAPA(D_ERROR, capa, "opc "LPX64" not supported by", opc);
182                 RETURN(rc);
183         }
184
185         oc = capa_lookup(filter->fo_capa_hash, capa, 0);
186         if (oc) {
187                 spin_lock(&oc->c_lock);
188                 if (capa_is_expired(oc)) {
189                         DEBUG_CAPA(D_ERROR, capa, "expired");
190                         rc = -ESTALE;
191                 }
192                 spin_unlock(&oc->c_lock);
193
194                 capa_put(oc);
195                 RETURN(rc);
196         }
197
198         if (capa_is_expired_sec(capa)) {
199                 DEBUG_CAPA(D_ERROR, capa, "expired");
200                 RETURN(-ESTALE);
201         }
202
203         spin_lock(&capa_lock);
204         list_for_each_entry(k, &filter->fo_capa_keys, k_list) {
205                 if (k->k_key.lk_seq == seq) {
206                         keys_ready = 1;
207                         if (k->k_key.lk_keyid == capa_keyid(capa)) {
208                                 key = k->k_key;
209                                 key_found = 1;
210                                 break;
211                         }
212                 }
213         }
214         spin_unlock(&capa_lock);
215
216         if (!keys_ready) {
217                 CDEBUG(D_SEC, "MDS hasn't propagated capability keys yet, "
218                        "ignore check!\n");
219                 RETURN(0);
220         }
221
222         if (!key_found) {
223                 DEBUG_CAPA(D_ERROR, capa, "no matched capability key for");
224                 RETURN(-ESTALE);
225         }
226
227         OBD_ALLOC(hmac, CAPA_HMAC_MAX_LEN);
228         if (hmac == NULL)
229                 RETURN(-ENOMEM);
230
231         rc = capa_hmac(hmac, capa, key.lk_key);
232         if (rc) {
233                 DEBUG_CAPA(D_ERROR, capa, "HMAC failed: rc %d", rc);
234                 OBD_FREE(hmac, CAPA_HMAC_MAX_LEN);
235                 RETURN(rc);
236         }
237
238         rc = memcmp(hmac, capa->lc_hmac, CAPA_HMAC_MAX_LEN);
239         OBD_FREE(hmac, CAPA_HMAC_MAX_LEN);
240         if (rc) {
241                 DEBUG_CAPA_KEY(D_ERROR, &key, "calculate HMAC with ");
242                 DEBUG_CAPA(D_ERROR, capa, "HMAC mismatch");
243                 RETURN(-EACCES);
244         }
245
246         /* store in capa hash */
247         oc = capa_add(filter->fo_capa_hash, capa);
248         capa_put(oc);
249         RETURN(0);
250 }
251
252 /**
253  * Free capability keys
254  *
255  * OFD free cached capability keys when OFD device is destroyed.
256  *
257  *  \param[in] ofd      OFD device
258  */
259 void ofd_free_capa_keys(struct ofd_device *ofd)
260 {
261         struct obd_device       *obd = ofd_obd(ofd);
262         struct filter_capa_key  *key, *n;
263
264         spin_lock(&capa_lock);
265         list_for_each_entry_safe(key, n, &obd->u.filter.fo_capa_keys, k_list) {
266                 list_del_init(&key->k_list);
267                 OBD_FREE_PTR(key);
268         }
269         spin_unlock(&capa_lock);
270 }