Whamcloud - gitweb
on OSS compare the whole capa structure because we might have 2 capa only
[fs/lustre-release.git] / lustre / obdfilter / filter_capa.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * Copyright (C) 2004, 2005 Cluster File Systems, Inc.
5  *
6  * Author: Lai Siyao <lsy@clusterfs.com>
7  *
8  *   This file is part of Lustre, http://www.lustre.org.
9  *
10  *   Lustre is free software; you can redistribute it and/or
11  *   modify it under the terms of version 2 of the GNU General Public
12  *   License as published by the Free Software Foundation.
13  *
14  *   Lustre is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *   GNU General Public License for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   along with Lustre; if not, write to the Free Software
21  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #define DEBUG_SUBSYSTEM S_FILTER
25
26 #include <linux/fs.h>
27 #include <linux/version.h>
28 #include <asm/uaccess.h>
29 #include <linux/file.h>
30 #include <linux/kmod.h>
31
32 #include <linux/lustre_fsfilt.h>
33 #include <linux/lustre_sec.h>
34
35 #include "filter_internal.h"
36
37 /*
38  * FIXME
39  * keep this as simple as possible. we suppose the blacklist usually
40  * be empry or very short (<5), since long term blacklist should be
41  * done on MDS side. A more sophisticated blacklist will be implemented
42  * later.
43  *
44  * note blacklist didn't take effect when OSS capability disabled. this
45  * looks reasonable to me.
46  */
47 #define BLACKLIST_MAX   (32)
48 static int nblacklist = 0;
49 static uid_t blacklist[BLACKLIST_MAX];
50 static spinlock_t blacklist_lock = SPIN_LOCK_UNLOCKED;
51
52 int blacklist_display(char *buf, int bufsize)
53 {
54         char one[16];
55         int i;
56         LASSERT(buf);
57
58         buf[0] = '\0';
59         spin_lock(&blacklist_lock);
60         for (i = 0; i < nblacklist; i++) {
61                 snprintf(one, 16, "%u\n", blacklist[i]);
62                 strncat(buf, one, bufsize);
63         }
64         spin_unlock(&blacklist_lock);
65         return strnlen(buf, bufsize);
66 }
67
68 void blacklist_add(uid_t uid)
69 {
70         int i;
71
72         spin_lock(&blacklist_lock);
73         if (nblacklist == BLACKLIST_MAX) {
74                 CERROR("can't add more in blacklist\n");
75                 spin_unlock(&blacklist_lock);
76                 return;
77         }
78
79         for (i = 0; i < nblacklist; i++) {
80                 if (blacklist[i] == uid) {
81                         spin_unlock(&blacklist_lock);
82                         return;
83                 }
84         }
85
86         blacklist[nblacklist++] = uid;
87         spin_unlock(&blacklist_lock);
88 }
89
90 void blacklist_del(uid_t uid)
91 {
92         int i;
93
94         spin_lock(&blacklist_lock);
95         for (i = 0; i < nblacklist; i++) {
96                 if (blacklist[i] == uid) {
97                         nblacklist--;
98                         while (i < nblacklist) {
99                                 blacklist[i] = blacklist[i+1];
100                                 i++;
101                         }
102                         spin_unlock(&blacklist_lock);
103                         return;
104                 }
105         }
106         spin_unlock(&blacklist_lock);
107 }
108
109 int blacklist_check(uid_t uid)
110 {
111         int i, rc = 0;
112
113         spin_lock(&blacklist_lock);
114         for (i = 0; i < nblacklist; i++) {
115                 if (blacklist[i] == uid) {
116                         rc = 1;
117                         break;
118                 }
119         }
120         spin_unlock(&blacklist_lock);
121         return rc;
122 }
123
124
125 void filter_free_capa_keys(struct filter_obd *filter)
126 {
127         struct filter_capa_key *key, *n;
128
129         spin_lock(&filter->fo_capa_lock);
130         list_for_each_entry_safe(key, n, &filter->fo_capa_keys, k_list) {
131                 list_del_init(&key->k_list);
132                 OBD_FREE(key, sizeof(*key));
133         }
134         spin_unlock(&filter->fo_capa_lock);
135 }
136
137 int filter_update_capa_key(struct obd_device *obd, struct lustre_capa_key *key)
138 {
139         struct filter_obd *filter = &obd->u.filter;
140         struct filter_capa_key *tmp = NULL, *rkey = NULL, *bkey = NULL;
141         int rc = 0;
142         ENTRY;
143
144         spin_lock(&filter->fo_capa_lock);
145         list_for_each_entry(tmp, &filter->fo_capa_keys, k_list) {
146                 if (tmp->k_key.lk_mdsid != le32_to_cpu(key->lk_mdsid))
147                         continue;
148
149                 if (rkey == NULL)
150                         rkey = tmp;
151                 else
152                         bkey = tmp;
153         }
154         spin_unlock(&filter->fo_capa_lock);
155
156         if (rkey && bkey && capa_key_cmp(&rkey->k_key, &bkey->k_key) < 0) {
157                 tmp = rkey;
158                 rkey = bkey;
159                 bkey = tmp;
160         }
161
162         if (bkey) {
163                 tmp = bkey;
164
165                 DEBUG_CAPA_KEY(D_INFO, &tmp->k_key, "filter update");
166         } else {
167                 OBD_ALLOC(tmp, sizeof(*tmp));
168                 if (!tmp)
169                         GOTO(out, rc = -ENOMEM);
170
171                 DEBUG_CAPA_KEY(D_INFO, &tmp->k_key, "filter new");
172         }
173
174         /* fields in lustre_capa_key are in cpu order */
175         spin_lock(&filter->fo_capa_lock);
176         tmp->k_key.lk_mdsid = le32_to_cpu(key->lk_mdsid);
177         tmp->k_key.lk_keyid = le32_to_cpu(key->lk_keyid);
178         tmp->k_key.lk_expiry = le64_to_cpu(key->lk_expiry);
179         memcpy(&tmp->k_key.lk_key, key->lk_key, sizeof(key->lk_key));
180
181         if (!bkey)
182                 list_add_tail(&tmp->k_list, &filter->fo_capa_keys);
183         spin_unlock(&filter->fo_capa_lock);
184 out:
185         RETURN(rc);
186 }
187
188 int filter_verify_fid(struct obd_export *exp, struct inode *inode,
189                       struct lustre_capa *capa)
190 {
191         struct lustre_id fid;
192         int rc;
193
194         if (!capa)
195                 return 0;
196
197         ENTRY;
198         rc = fsfilt_get_md(exp->exp_obd, inode, &fid, sizeof(fid), EA_SID);
199         if (rc < 0) {
200                 CERROR("get fid from object failed! rc:%d\n", rc);
201                 RETURN(rc);
202         } else if (rc > 0) {
203                 if (capa->lc_mdsid != id_group(&fid) ||
204                     capa->lc_ino != id_ino(&fid))
205                         RETURN(-EINVAL);
206         }
207
208         RETURN(0);
209 }
210
211 static void dump_capa_hmac(char *buf, char *key)
212 {
213         int i, n = 0;
214
215         for (i = 0; i < CAPA_DIGEST_SIZE; i++)
216                 n += sprintf(buf + n, "%02x", (unsigned char) key[i]);
217 }
218
219 int
220 filter_verify_capa(int cmd, struct obd_export *exp, struct lustre_capa *capa)
221 {
222         struct obd_device *obd = exp->exp_obd;
223         struct filter_obd *filter = &obd->u.filter;
224         struct obd_capa *ocapa;
225         struct lustre_capa tcapa;
226         struct filter_capa_key *rkey = NULL, *bkey = NULL, *tmp, capa_keys[2];
227         int rc = 0;
228
229         /* capability is disabled */
230         if (filter->fo_capa_stat == 0)
231                 RETURN(0);
232
233         ENTRY;
234         if (capa == NULL) {
235                 CDEBUG(D_ERROR, "no capa has been passed\n");
236                 RETURN(-EACCES);
237         }
238
239         if (blacklist_check(capa->lc_uid)) {
240                 DEBUG_CAPA(D_ERROR, capa, "found in blacklist\n");
241                 RETURN(-EACCES);
242         }
243
244         if (cmd == OBD_BRW_WRITE && !(capa->lc_op & (CAPA_WRITE | CAPA_TRUNC))) {
245                 DEBUG_CAPA(D_ERROR, capa, "have no write access\n");
246                 RETURN(-EACCES);
247         }
248         if (cmd == OBD_BRW_READ && !(capa->lc_op & (CAPA_WRITE | CAPA_READ))) {
249                 DEBUG_CAPA(D_ERROR, capa, "have no read access\n");
250                 RETURN(-EACCES);
251         }
252
253         if (OBD_FAIL_CHECK(OBD_FAIL_FILTER_VERIFY_CAPA))
254                 RETURN(-EACCES);
255
256         if (capa_expired(capa)) {
257                 DEBUG_CAPA(D_INFO | D_ERROR, capa, "expired");
258                 RETURN(-ESTALE);
259         }
260
261         ocapa = filter_capa_get(capa);
262 verify:
263         if (ocapa) {
264                 struct timeval tv;
265
266                 /* fo_capa_lock protects capa too */
267                 do_gettimeofday(&tv);
268                 spin_lock(&filter->fo_capa_lock);
269                 if (capa->lc_keyid == ocapa->c_capa.lc_keyid) {
270                         rc = memcmp(capa, &ocapa->c_capa, sizeof(*capa));
271                 } else if (ocapa->c_bvalid &&
272                            capa->lc_keyid == ocapa->c_bkeyid) {
273                         rc = memcmp(capa->lc_hmac, ocapa->c_bhmac,
274                                     sizeof(capa->lc_hmac));
275                 } else {
276                         /* ocapa is obsolete too */
277                         ocapa->c_bvalid = 0;
278                         goto new_capa;
279                 }
280
281                 if (rc && __capa_is_to_expire(ocapa, &tv)) {
282                         /* client should use new expiry now */
283                         ocapa->c_bvalid = 0;
284                         goto new_capa;
285                 }
286                 spin_unlock(&filter->fo_capa_lock);
287
288                 if (rc) {
289                         char *key1 = NULL, *key2 = NULL;
290                         OBD_ALLOC(key1, CAPA_DIGEST_SIZE * 2 + 1);
291                         OBD_ALLOC(key2, CAPA_DIGEST_SIZE * 2 + 1);
292                         if (key1 && key2) {
293                                 dump_capa_hmac(key1, capa->lc_hmac);
294                                 dump_capa_hmac(key2, ocapa->c_capa.lc_hmac);
295                                 DEBUG_CAPA(D_ERROR, capa,
296                                            "access denied for (%s != %s)",
297                                            key1, key2);
298                                 DEBUG_CAPA(D_ERROR, &ocapa->c_capa, "used capa");
299                         }
300                         if (key1)
301                                 OBD_FREE(key1, CAPA_DIGEST_SIZE * 2 + 1);
302                         if (key2)
303                                 OBD_FREE(key2, CAPA_DIGEST_SIZE * 2 + 1);
304                 }
305                 capa_put(ocapa);
306                 RETURN(rc ? -EACCES : 0);
307         }
308
309         spin_lock(&filter->fo_capa_lock);
310 new_capa:
311         list_for_each_entry(tmp, &filter->fo_capa_keys, k_list) {
312                 if (tmp->k_key.lk_mdsid == capa->lc_mdsid) {
313                         if (rkey == NULL)
314                                 rkey = tmp;
315                         else
316                                 bkey = tmp;
317                 }
318         }
319
320         if (rkey && bkey && capa_key_cmp(&rkey->k_key, &bkey->k_key) < 0) {
321                 tmp = rkey;
322                 rkey = bkey;
323                 bkey = tmp;
324         }
325
326         if ((!rkey || rkey->k_key.lk_keyid != capa->lc_keyid) &&
327             (!bkey || bkey->k_key.lk_keyid != capa->lc_keyid)) {
328                 spin_unlock(&filter->fo_capa_lock);
329                 GOTO(out, rc = -ESTALE);
330         }
331
332         LASSERT(rkey);
333         capa_keys[0] = *rkey;
334         if (bkey)
335                 capa_keys[1] = *bkey;
336         spin_unlock(&filter->fo_capa_lock);
337
338         tcapa = *capa;
339         tcapa.lc_keyid = capa_keys[0].k_key.lk_keyid;
340         capa_hmac(filter->fo_capa_hmac, capa_keys[0].k_key.lk_key, &tcapa);
341
342         /* store in capa cache */
343         ocapa = capa_renew(&tcapa, FILTER_CAPA);
344         if (!ocapa)
345                 GOTO(out, rc = -ENOMEM);
346
347         if (bkey) {
348                 tcapa.lc_keyid = capa_keys[1].k_key.lk_keyid;
349                 capa_hmac(filter->fo_capa_hmac, capa_keys[1].k_key.lk_key,
350                           &tcapa);
351
352                 spin_lock(&filter->fo_capa_lock);
353                 memcpy(ocapa->c_bhmac, tcapa.lc_hmac, sizeof(ocapa->c_bhmac));
354                 ocapa->c_bkeyid = capa_keys[1].k_key.lk_keyid;
355                 ocapa->c_bvalid = 1;
356                 spin_unlock(&filter->fo_capa_lock);
357         }
358         goto verify;
359 out:
360         RETURN(rc);
361 }