Whamcloud - gitweb
land b_hd_sec onto HEAD: various security related fixes.
[fs/lustre-release.git] / lustre / sec / sec.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * Copyright (C) 2004 Cluster File Systems, Inc.
5  *
6  *   This file is part of Lustre, http://www.lustre.org.
7  *
8  *   Lustre is free software; you can redistribute it and/or
9  *   modify it under the terms of version 2 of the GNU General Public
10  *   License as published by the Free Software Foundation.
11  *
12  *   Lustre is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with Lustre; if not, write to the Free Software
19  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #ifndef EXPORT_SYMTAB
23 # define EXPORT_SYMTAB
24 #endif
25 #define DEBUG_SUBSYSTEM S_SEC
26 #ifdef __KERNEL__
27 #include <linux/init.h>
28 #include <linux/module.h>
29 #include <linux/slab.h>
30 #else
31 #include <liblustre.h>
32 #endif
33
34 #include <libcfs/kp30.h>
35 #include <linux/obd.h>
36 #include <linux/obd_class.h>
37 #include <linux/obd_support.h>
38 #include <linux/lustre_net.h>
39 #include <linux/lustre_import.h>
40 #include <linux/lustre_dlm.h>
41 #include <linux/lustre_sec.h>
42
43 static spinlock_t sectypes_lock = SPIN_LOCK_UNLOCKED;
44 static struct ptlrpc_sec_type *sectypes[PTLRPC_SEC_MAX_FLAVORS] = {
45         NULL,
46 };
47
48 int ptlrpcs_register(struct ptlrpc_sec_type *type)
49 {
50         __u32 flavor = type->pst_flavor.flavor;
51
52         LASSERT(type->pst_name);
53         LASSERT(type->pst_ops);
54
55         if (flavor >= PTLRPC_SEC_MAX_FLAVORS)
56                 return -EINVAL;
57
58         spin_lock(&sectypes_lock);
59         if (sectypes[flavor]) {
60                 spin_unlock(&sectypes_lock);
61                 return -EALREADY;
62         }
63         sectypes[flavor] = type;
64         atomic_set(&type->pst_inst, 0);
65         spin_unlock(&sectypes_lock);
66
67         CWARN("Security module %s registered\n", type->pst_name);
68         return 0;
69 }
70
71 int ptlrpcs_unregister(struct ptlrpc_sec_type *type)
72 {
73         __u32 flavor = type->pst_flavor.flavor;
74
75         if (flavor >= PTLRPC_SEC_MAX_FLAVORS)
76                 return -EINVAL;
77
78         spin_lock(&sectypes_lock);
79         if (!sectypes[flavor]) {
80                 spin_unlock(&sectypes_lock);
81                 return -EINVAL;
82         }
83
84         if (sectypes[flavor] != type) {
85                 CERROR("invalid unregister\n");
86                 return -EINVAL;
87         }
88
89         if (atomic_read(&type->pst_inst)) {
90                 CERROR("sec module %s still have instance %d\n",
91                        type->pst_name, atomic_read(&type->pst_inst));
92                 spin_unlock(&sectypes_lock);
93                 return -EINVAL;
94         }
95
96         CDEBUG(D_SEC, "Security module %s unregistered\n", type->pst_name);
97         sectypes[flavor] = NULL;
98         spin_unlock(&sectypes_lock);
99
100         return 0;
101 }
102
103 static
104 struct ptlrpc_sec_type * ptlrpcs_flavor2type(ptlrpcs_flavor_t *flavor)
105 {
106         struct ptlrpc_sec_type *type;
107         __u32 major = flavor->flavor;
108
109         if (major >= PTLRPC_SEC_MAX_FLAVORS)
110                 return NULL;
111
112         spin_lock(&sectypes_lock);
113         type = sectypes[major];
114         if (type && !try_module_get(type->pst_owner))
115                 type = NULL;
116         spin_unlock(&sectypes_lock);
117         return type;
118 }
119
120 static inline
121 void ptlrpcs_type_put(struct ptlrpc_sec_type *type)
122 {
123         module_put(type->pst_owner);
124 }
125
126 /***********************************************
127  * credential cache helpers                    *
128  ***********************************************/
129
130 void ptlrpcs_init_credcache(struct ptlrpc_sec *sec)
131 {
132         int i;
133         for (i = 0; i < PTLRPC_CREDCACHE_NR; i++)
134                 INIT_LIST_HEAD(&sec->ps_credcache[i]);
135         sec->ps_nextgc = get_seconds() + (sec->ps_expire >> 1);
136 }
137
138 static void ptlrpcs_cred_destroy(struct ptlrpc_cred *cred)
139 {
140         struct ptlrpc_sec *sec = cred->pc_sec;
141
142         LASSERT(cred->pc_sec);
143         LASSERT(atomic_read(&cred->pc_refcount) == 0);
144         LASSERT(list_empty(&cred->pc_hash));
145
146         cred->pc_ops->destroy(cred);
147         atomic_dec(&sec->ps_credcount);
148 }
149
150 static void ptlrpcs_destroy_credlist(struct list_head *head)
151 {
152         struct ptlrpc_cred *cred;
153
154         while (!list_empty(head)) {
155                 cred = list_entry(head->next, struct ptlrpc_cred, pc_hash);
156                 list_del_init(&cred->pc_hash);
157                 ptlrpcs_cred_destroy(cred);
158         }
159 }
160
161 static
162 int ptlrpcs_cred_unlink_expired(struct ptlrpc_cred *cred,
163                                 struct list_head *freelist)
164 {
165         LASSERT(cred->pc_sec);
166
167         /* only unlink non-busy entries */
168         if (atomic_read(&cred->pc_refcount) != 0)
169                 return 0;
170         /* expire is 0 means never expire. a newly created gss cred
171          * which during upcall also has 0 expiration
172          */
173         if (cred->pc_expire == 0)
174                 return 0;
175         /* check real expiration */
176         if (time_after(cred->pc_expire, get_seconds()))
177                 return 0;
178
179         LASSERT((cred->pc_flags & PTLRPC_CRED_FLAGS_MASK) ==
180                 PTLRPC_CRED_UPTODATE);
181         CWARN("cred %p: get expired, unlink\n", cred);
182
183         list_del(&cred->pc_hash);
184         list_add(&cred->pc_hash, freelist);
185         return 1;
186 }
187
188 static
189 void ptlrpcs_credcache_gc(struct ptlrpc_sec *sec,
190                           struct list_head *freelist)
191 {
192         struct ptlrpc_cred *cred, *n;
193         int i;
194         ENTRY;
195
196         CDEBUG(D_SEC, "do gc on sec %s\n", sec->ps_type->pst_name);
197         for (i = 0; i < PTLRPC_CREDCACHE_NR; i++) {
198                 list_for_each_entry_safe(cred, n, &sec->ps_credcache[i],
199                                          pc_hash) {
200                         if (cred->pc_flags & (PTLRPC_CRED_DEAD |
201                                               PTLRPC_CRED_ERROR)) {
202                                 LASSERT(atomic_read(&cred->pc_refcount));
203                                 continue;
204                         }
205                         ptlrpcs_cred_unlink_expired(cred, freelist);
206                 }
207         }
208         sec->ps_nextgc = get_seconds() + sec->ps_expire;
209         EXIT;
210 }
211
212 static
213 int ptlrpcs_flush_credcache(struct ptlrpc_sec *sec, int force)
214 {
215         struct ptlrpc_cred *cred, *n;
216         LIST_HEAD(freelist);
217         int i, busy = 0;
218         ENTRY;
219
220         spin_lock(&sec->ps_lock);
221         for (i = 0; i < PTLRPC_CREDCACHE_NR; i++) {
222                 list_for_each_entry_safe(cred, n, &sec->ps_credcache[i],
223                                          pc_hash) {
224                         LASSERT(atomic_read(&cred->pc_refcount) >= 0);
225                         if (atomic_read(&cred->pc_refcount)) {
226                                 busy = 1;
227                                 if (!force)
228                                         continue;
229                                 list_del_init(&cred->pc_hash);
230                         } else
231                                 list_move(&cred->pc_hash, &freelist);
232
233                         /* don't remove CRED_UPTODATE flag here */
234                         cred->pc_flags |= PTLRPC_CRED_DEAD;
235                 }
236         }
237         spin_unlock(&sec->ps_lock);
238         ptlrpcs_destroy_credlist(&freelist);
239         RETURN(busy);
240 }
241
242 /**************************************************
243  * credential APIs                                *
244  **************************************************/
245
246 static inline
247 int ptlrpcs_cred_get_hash(__u64 pag)
248 {
249         LASSERT((pag & PTLRPC_CREDCACHE_MASK) < PTLRPC_CREDCACHE_NR);
250         return (pag & PTLRPC_CREDCACHE_MASK);
251 }
252
253 static
254 struct ptlrpc_cred * cred_cache_lookup(struct ptlrpc_sec *sec,
255                                        struct vfs_cred *vcred,
256                                        int create)
257 {
258         struct ptlrpc_cred *cred, *new = NULL, *n;
259         LIST_HEAD(freelist);
260         int hash, found = 0;
261         ENTRY;
262
263         hash = ptlrpcs_cred_get_hash(vcred->vc_pag);
264
265 retry:
266         spin_lock(&sec->ps_lock);
267         /* do gc if expired */
268         if (time_after(get_seconds(), sec->ps_nextgc))
269                 ptlrpcs_credcache_gc(sec, &freelist);
270
271         list_for_each_entry_safe(cred, n, &sec->ps_credcache[hash], pc_hash) {
272                 /* for DEAD and ERROR entries, its final put will
273                  * release them, so we simply skip here.
274                  */
275                 if (cred->pc_flags & (PTLRPC_CRED_DEAD | PTLRPC_CRED_ERROR)) {
276                         LASSERT(atomic_read(&cred->pc_refcount));
277                         continue;
278                 }
279                 if (ptlrpcs_cred_unlink_expired(cred, &freelist))
280                         continue;
281                 if (cred->pc_ops->match(cred, vcred)) {
282                         found = 1;
283                         break;
284                 }
285         }
286
287         if (found) {
288                 if (new && new != cred) {
289                         /* lost the race, just free it */
290                         list_add(&new->pc_hash, &freelist);
291                 }
292                 list_move(&cred->pc_hash, &sec->ps_credcache[hash]);
293         } else {
294                 if (new) {
295                         list_add(&new->pc_hash, &sec->ps_credcache[hash]);
296                         cred = new;
297                 } else if (create) {
298                         spin_unlock(&sec->ps_lock);
299                         new = sec->ps_type->pst_ops->create_cred(sec, vcred);
300                         if (new) {
301                                 atomic_inc(&sec->ps_credcount);
302                                 goto retry;
303                         }
304                 } else
305                         cred = NULL;
306         }
307
308         /* hold a ref */
309         if (cred)
310                 atomic_inc(&cred->pc_refcount);
311
312         spin_unlock(&sec->ps_lock);
313
314         ptlrpcs_destroy_credlist(&freelist);
315         RETURN(cred);
316 }
317
318 struct ptlrpc_cred * ptlrpcs_cred_lookup(struct ptlrpc_sec *sec,
319                                          struct vfs_cred *vcred)
320 {
321         struct ptlrpc_cred *cred;
322         ENTRY;
323
324         cred = cred_cache_lookup(sec, vcred, 0);
325         RETURN(cred);
326 }
327
328 static struct ptlrpc_cred *get_cred(struct ptlrpc_sec *sec)
329 {
330         struct vfs_cred vcred;
331
332         LASSERT(sec);
333         /* XXX
334          * for now we simply let PAG == real uid
335          */
336         vcred.vc_pag = (__u64) current->uid;
337         vcred.vc_uid = current->uid;
338
339         return cred_cache_lookup(sec, &vcred, 1);
340 }
341
342 int ptlrpcs_req_get_cred(struct ptlrpc_request *req)
343 {
344         struct obd_import *imp = req->rq_import;
345         ENTRY;
346
347         LASSERT(!req->rq_cred);
348         LASSERT(imp);
349
350         req->rq_cred = get_cred(imp->imp_sec);
351
352         if (!req->rq_cred) {
353                 CERROR("req %p: fail to get cred from cache\n", req);
354                 RETURN(-ENOMEM);
355         }
356
357         RETURN(0);
358 }
359
360 /*
361  * check whether current user have valid credential for an import or not.
362  * might repeatedly try in case of non-fatal errors.
363  * return 0 on success, 1 on failure
364  */
365 int ptlrpcs_check_cred(struct obd_import *imp)
366 {
367         struct ptlrpc_cred *cred;
368         ENTRY;
369
370 again:
371         cred = get_cred(imp->imp_sec);
372         if (!cred)
373                 RETURN(0);
374
375         if (ptlrpcs_cred_is_uptodate(cred)) {
376                 if (!ptlrpcs_cred_check_expire(cred)) {
377                         ptlrpcs_cred_put(cred, 1);
378                         RETURN(0);
379                 } else {
380                         ptlrpcs_cred_put(cred, 1);
381                         goto again;
382                 }
383         }
384
385         ptlrpcs_cred_refresh(cred);
386         if (ptlrpcs_cred_is_uptodate(cred)) {
387                 ptlrpcs_cred_put(cred, 1);
388                 RETURN(0);
389         }
390
391         if (cred->pc_flags & PTLRPC_CRED_ERROR ||
392             !imp->imp_replayable) {
393                 ptlrpcs_cred_put(cred, 1);
394                 RETURN(1);
395         }
396
397         ptlrpcs_cred_put(cred, 1);
398
399         if (signal_pending(current)) {
400                 CWARN("%s: interrupted\n", current->comm);
401                 RETURN(1);
402         }
403         goto again;
404 }
405
406 static void ptlrpcs_sec_destroy(struct ptlrpc_sec *sec);
407
408 void ptlrpcs_cred_put(struct ptlrpc_cred *cred, int sync)
409 {
410         struct ptlrpc_sec *sec = cred->pc_sec;
411
412         LASSERT(cred);
413         LASSERT(sec);
414         LASSERT(atomic_read(&cred->pc_refcount));
415
416         spin_lock(&sec->ps_lock);
417         if (atomic_dec_and_test(&cred->pc_refcount) && sync &&
418             cred->pc_flags & (PTLRPC_CRED_DEAD | PTLRPC_CRED_ERROR)) {
419                 list_del_init(&cred->pc_hash);
420                 ptlrpcs_cred_destroy(cred);
421                 if (!atomic_read(&sec->ps_credcount) &&
422                     !atomic_read(&sec->ps_refcount)) {
423                         CWARN("put last cred on a dead sec %p(%s), "
424                               "also destroy the sec\n", sec,
425                                sec->ps_type->pst_name);
426                         spin_unlock(&sec->ps_lock);
427
428                         ptlrpcs_sec_destroy(sec);
429                         return;
430                 }
431         }
432         spin_unlock(&sec->ps_lock);
433 }
434
435 void ptlrpcs_req_drop_cred(struct ptlrpc_request *req)
436 {
437         ENTRY;
438
439         LASSERT(req);
440         LASSERT(req->rq_cred);
441
442         if (req->rq_cred) {
443                 /* We'd like to not use 'sync' mode, but might cause
444                  * some cred leak. Need more thinking here. FIXME
445                  */
446                 ptlrpcs_cred_put(req->rq_cred, 1);
447                 req->rq_cred = NULL;
448         } else
449                 CDEBUG(D_SEC, "req %p have no cred\n", req);
450         EXIT;
451 }
452
453 /* 
454  * request must have a cred. if failed to get new cred,
455  * just restore the old one
456  */
457 int ptlrpcs_req_replace_dead_cred(struct ptlrpc_request *req)
458 {
459         struct ptlrpc_cred *cred = req->rq_cred;
460         int rc;
461         ENTRY;
462
463         LASSERT(cred);
464         LASSERT(cred->pc_flags & PTLRPC_CRED_DEAD);
465
466         ptlrpcs_cred_get(cred);
467         ptlrpcs_req_drop_cred(req);
468         LASSERT(!req->rq_cred);
469         rc = ptlrpcs_req_get_cred(req);
470         if (!rc) {
471                 LASSERT(req->rq_cred);
472                 LASSERT(req->rq_cred != cred);
473                 ptlrpcs_cred_put(cred, 1);
474         } else {
475                 LASSERT(!req->rq_cred);
476                 req->rq_cred = cred;
477         }
478         RETURN(rc);
479 }
480
481 /*
482  * since there's no lock on the cred, its status could be changed
483  * by other threads at any time, we allow this race.
484  */
485 int ptlrpcs_req_refresh_cred(struct ptlrpc_request *req)
486 {
487         struct ptlrpc_cred *cred = req->rq_cred;
488         ENTRY;
489
490         LASSERT(cred);
491
492         if (ptlrpcs_cred_is_uptodate(cred)) {
493                 if (!ptlrpcs_cred_check_expire(cred))
494                         RETURN(0);
495         }
496
497         if (cred->pc_flags & PTLRPC_CRED_ERROR) {
498                 req->rq_ptlrpcs_err = 1;
499                 RETURN(-EPERM);
500         }
501
502         if (cred->pc_flags & PTLRPC_CRED_DEAD) {
503                 if (ptlrpcs_req_replace_dead_cred(req) == 0) {
504                         LASSERT(cred != req->rq_cred);
505                         CDEBUG(D_SEC, "req %p: replace cred %p => %p\n",
506                                req, cred, req->rq_cred);
507                         cred = req->rq_cred;
508                 } else {
509                         LASSERT(cred == req->rq_cred);
510                         CERROR("req %p: failed to replace dead cred %p\n",
511                                 req, cred);
512                         req->rq_ptlrpcs_err = 1;
513                         RETURN(-ENOMEM);
514                 }
515         }
516
517         ptlrpcs_cred_refresh(cred);
518         if (!ptlrpcs_cred_is_uptodate(cred)) {
519                 if (cred->pc_flags & PTLRPC_CRED_ERROR)
520                         req->rq_ptlrpcs_err = 1;
521
522                 CERROR("req %p: failed to refresh cred %p, fatal %d\n",
523                         req, cred, req->rq_ptlrpcs_err);
524                 RETURN(-EPERM);
525         } else
526                 RETURN(0);
527 }
528
529 int ptlrpcs_cli_wrap_request(struct ptlrpc_request *req)
530 {
531         struct ptlrpc_cred     *cred;
532         int rc;
533         ENTRY;
534
535         LASSERT(req->rq_cred);
536         LASSERT(req->rq_cred->pc_sec);
537         LASSERT(req->rq_cred->pc_ops);
538         LASSERT(req->rq_reqbuf);
539         LASSERT(req->rq_reqbuf_len);
540
541         rc = ptlrpcs_req_refresh_cred(req);
542         if (rc)
543                 RETURN(rc);
544
545         CDEBUG(D_SEC, "wrap req %p\n", req);
546         cred = req->rq_cred;
547
548         switch (cred->pc_sec->ps_sectype) {
549         case PTLRPC_SEC_TYPE_NONE:
550         case PTLRPC_SEC_TYPE_AUTH:
551                 if (req->rq_req_wrapped) {
552                         CDEBUG(D_SEC, "req %p(o%u,x"LPU64",t"LPU64") "
553                                "already signed, resend?\n", req,
554                                req->rq_reqmsg ? req->rq_reqmsg->opc : -1,
555                                req->rq_xid, req->rq_transno);
556                         req->rq_req_wrapped = 0;
557                         req->rq_reqdata_len = sizeof(struct ptlrpcs_wire_hdr) +
558                                               req->rq_reqlen;
559                         LASSERT(req->rq_reqdata_len % 8 == 0);
560                 }
561
562                 LASSERT(cred->pc_ops->sign);
563                 rc = cred->pc_ops->sign(cred, req);
564                 if (!rc)
565                         req->rq_req_wrapped = 1;
566                 break;
567         case PTLRPC_SEC_TYPE_PRIV:
568                 if (req->rq_req_wrapped) {
569                         CDEBUG(D_SEC, "req %p(o%u,x"LPU64",t"LPU64") "
570                                "already encrypted, resend?\n", req,
571                                req->rq_reqmsg ? req->rq_reqmsg->opc : -1,
572                                req->rq_xid, req->rq_transno);
573                         req->rq_req_wrapped = 0;
574                         req->rq_reqdata_len = sizeof(struct ptlrpcs_wire_hdr);
575                         LASSERT(req->rq_reqdata_len % 8 == 0);
576                 }
577
578                 LASSERT(cred->pc_ops->seal);
579                 rc = cred->pc_ops->seal(cred, req);
580                 if (!rc)
581                         req->rq_req_wrapped = 1;
582                 break;
583         default:
584                 LBUG();
585         }
586         LASSERT(req->rq_reqdata_len);
587         LASSERT(req->rq_reqdata_len % 8 == 0);
588         LASSERT(req->rq_reqdata_len >= sizeof(struct ptlrpcs_wire_hdr));
589         LASSERT(req->rq_reqdata_len <= req->rq_reqbuf_len);
590
591         RETURN(rc);
592 }
593
594 /* rq_nob_received is the actual received data length */
595 int ptlrpcs_cli_unwrap_reply(struct ptlrpc_request *req)
596 {
597         struct ptlrpc_cred *cred = req->rq_cred;
598         struct ptlrpc_sec *sec;
599         struct ptlrpcs_wire_hdr *sec_hdr;
600         int rc;
601         ENTRY;
602
603         LASSERT(cred);
604         LASSERT(cred->pc_sec);
605         LASSERT(cred->pc_ops);
606         LASSERT(req->rq_repbuf);
607         
608         if (req->rq_nob_received < sizeof(*sec_hdr)) {
609                 CERROR("req %p: reply size only %d\n",
610                         req, req->rq_nob_received);
611                 RETURN(-EPROTO);
612         }
613
614         sec_hdr = (struct ptlrpcs_wire_hdr *) req->rq_repbuf;
615         sec_hdr->flavor = le32_to_cpu(sec_hdr->flavor);
616         sec_hdr->sectype = le32_to_cpu(sec_hdr->sectype);
617         sec_hdr->msg_len = le32_to_cpu(sec_hdr->msg_len);
618         sec_hdr->sec_len = le32_to_cpu(sec_hdr->sec_len);
619
620         CDEBUG(D_SEC, "req %p, cred %p, flavor %u, sectype %u\n",
621                req, cred, sec_hdr->flavor, sec_hdr->sectype);
622
623         sec = cred->pc_sec;
624         if (sec_hdr->flavor != sec->ps_flavor.flavor) {
625                 CERROR("unmatched flavor %u while expect %u\n",
626                        sec_hdr->flavor, sec->ps_flavor.flavor);
627                 RETURN(-EPROTO);
628         }
629
630         if (sizeof(*sec_hdr) + sec_hdr->msg_len + sec_hdr->sec_len >
631             req->rq_nob_received) {
632                 CERROR("msg %u, sec %u, while only get %d\n",
633                         sec_hdr->msg_len, sec_hdr->sec_len,
634                         req->rq_nob_received);
635                 RETURN(-EPROTO);
636         }
637
638         switch (sec_hdr->sectype) {
639         case PTLRPC_SEC_TYPE_NONE:
640         case PTLRPC_SEC_TYPE_AUTH: {
641                 LASSERT(cred->pc_ops->verify);
642                 rc = cred->pc_ops->verify(cred, req);
643                 LASSERT(rc || req->rq_repmsg || req->rq_ptlrpcs_restart);
644                 break;
645         case PTLRPC_SEC_TYPE_PRIV:
646                 LASSERT(cred->pc_ops->unseal);
647                 rc = cred->pc_ops->unseal(cred, req);
648                 LASSERT(rc || req->rq_repmsg || req->rq_ptlrpcs_restart);
649                 break;
650         }
651         default:
652                 rc = -1;
653                 LBUG();
654         }
655         RETURN(rc);
656 }
657
658 /**************************************************
659  * security APIs                                  *
660  **************************************************/
661
662 struct ptlrpc_sec * ptlrpcs_sec_create(ptlrpcs_flavor_t *flavor,
663                                        struct obd_import *import,
664                                        const char *pipe_dir,
665                                        void *pipe_data)
666 {
667         struct ptlrpc_sec_type *type;
668         struct ptlrpc_sec *sec;
669         ENTRY;
670
671         type = ptlrpcs_flavor2type(flavor);
672         if (!type) {
673                 CDEBUG(D_SEC, "invalid major flavor %u\n", flavor->flavor);
674                 RETURN(NULL);
675         }
676
677         sec = type->pst_ops->create_sec(flavor, pipe_dir, pipe_data);
678         if (sec) {
679                 spin_lock_init(&sec->ps_lock);
680                 ptlrpcs_init_credcache(sec);
681                 sec->ps_type = type;
682                 sec->ps_flavor = *flavor;
683                 sec->ps_import = class_import_get(import);
684                 atomic_set(&sec->ps_refcount, 1);
685                 atomic_set(&sec->ps_credcount, 0);
686                 atomic_inc(&type->pst_inst);
687         } else
688                 ptlrpcs_type_put(type);
689
690         return sec;
691 }
692
693 static void ptlrpcs_sec_destroy(struct ptlrpc_sec *sec)
694 {
695         struct ptlrpc_sec_type *type = sec->ps_type;
696         struct obd_import *imp = sec->ps_import;
697
698         LASSERT(type && type->pst_ops);
699         LASSERT(type->pst_ops->destroy_sec);
700
701         type->pst_ops->destroy_sec(sec);
702         atomic_dec(&type->pst_inst);
703         ptlrpcs_type_put(type);
704         class_import_put(imp);
705 }
706
707 void ptlrpcs_sec_put(struct ptlrpc_sec *sec)
708 {
709         if (atomic_dec_and_test(&sec->ps_refcount)) {
710                 ptlrpcs_flush_credcache(sec, 1);
711
712                 if (atomic_read(&sec->ps_credcount) == 0) {
713                         ptlrpcs_sec_destroy(sec);
714                 } else {
715                         CWARN("sec %p(%s) is no usage while %d cred still "
716                               "holded, destroy delayed\n",
717                                sec, sec->ps_type->pst_name,
718                                atomic_read(&sec->ps_credcount));
719                 }
720         }
721 }
722
723 void ptlrpcs_sec_invalidate_cache(struct ptlrpc_sec *sec)
724 {
725         ptlrpcs_flush_credcache(sec, 1);
726 }
727
728 int sec_alloc_reqbuf(struct ptlrpc_sec *sec,
729                      struct ptlrpc_request *req,
730                      int msgsize, int secsize)
731 {
732         struct ptlrpcs_wire_hdr *hdr;
733         ENTRY;
734
735         LASSERT(msgsize % 8 == 0);
736         LASSERT(secsize % 8 == 0);
737
738         req->rq_reqbuf_len = sizeof(*hdr) + msgsize + secsize;
739         OBD_ALLOC(req->rq_reqbuf, req->rq_reqbuf_len);
740         if (!req->rq_reqbuf) {
741                 CERROR("can't alloc %d\n", req->rq_reqbuf_len);
742                 RETURN(-ENOMEM);
743         }
744
745         hdr = buf_to_sec_hdr(req->rq_reqbuf);
746         hdr->flavor = cpu_to_le32(sec->ps_flavor.flavor);
747         hdr->sectype = cpu_to_le32(sec->ps_sectype);
748         hdr->msg_len = msgsize;
749         /* security length will be filled later */
750
751         /* later reqdata_len will be added on actual security payload */
752         req->rq_reqdata_len = sizeof(*hdr) + msgsize;
753         req->rq_reqmsg = buf_to_lustre_msg(req->rq_reqbuf);
754
755         CDEBUG(D_SEC, "req %p: rqbuf at %p, len %d, msg %d, sec %d\n",
756                req, req->rq_reqbuf, req->rq_reqbuf_len,
757                msgsize, secsize);
758
759         RETURN(0);
760 }
761
762 /* when complete successfully, req->rq_reqmsg should point to the
763  * right place.
764  */
765 int ptlrpcs_cli_alloc_reqbuf(struct ptlrpc_request *req, int msgsize)
766 {
767         struct ptlrpc_cred *cred = req->rq_cred;
768         struct ptlrpc_sec *sec;
769         struct ptlrpc_secops *ops;
770
771         LASSERT(msgsize % 8 == 0);
772         LASSERT(sizeof(struct ptlrpcs_wire_hdr) % 8 == 0);
773         LASSERT(cred);
774         LASSERT(atomic_read(&cred->pc_refcount));
775         LASSERT(cred->pc_sec);
776         LASSERT(cred->pc_sec->ps_type);
777         LASSERT(cred->pc_sec->ps_type->pst_ops);
778         LASSERT(req->rq_reqbuf == NULL);
779         LASSERT(req->rq_reqmsg == NULL);
780
781         sec = cred->pc_sec;
782         ops = sec->ps_type->pst_ops;
783         if (ops->alloc_reqbuf)
784                 return ops->alloc_reqbuf(sec, req, msgsize);
785         else
786                 return sec_alloc_reqbuf(sec, req, msgsize, 0);
787 }
788
789 void sec_free_reqbuf(struct ptlrpc_sec *sec,
790                      struct ptlrpc_request *req)
791 {
792         LASSERT(req->rq_reqbuf);
793         LASSERT(req->rq_reqbuf_len);
794
795         /* sanity check */
796         if (req->rq_reqmsg) {
797                 LASSERT((char *) req->rq_reqmsg >= req->rq_reqbuf &&
798                         (char *) req->rq_reqmsg < req->rq_reqbuf +
799                                                   req->rq_reqbuf_len);
800         }
801
802         OBD_FREE(req->rq_reqbuf, req->rq_reqbuf_len);
803         req->rq_reqbuf = NULL;
804         req->rq_reqmsg = NULL;
805 }
806
807 void ptlrpcs_cli_free_reqbuf(struct ptlrpc_request *req)
808 {
809         struct ptlrpc_cred *cred = req->rq_cred;
810         struct ptlrpc_sec *sec;
811         struct ptlrpc_secops *ops;
812
813         LASSERT(cred);
814         LASSERT(atomic_read(&cred->pc_refcount));
815         LASSERT(cred->pc_sec);
816         LASSERT(cred->pc_sec->ps_type);
817         LASSERT(cred->pc_sec->ps_type->pst_ops);
818         LASSERT(req->rq_reqbuf);
819
820         sec = cred->pc_sec;
821         ops = sec->ps_type->pst_ops;
822         if (ops->free_reqbuf)
823                 ops->free_reqbuf(sec, req);
824         else
825                 sec_free_reqbuf(sec, req);
826 }
827
828 int ptlrpcs_cli_alloc_repbuf(struct ptlrpc_request *req, int msgsize)
829 {
830         struct ptlrpc_cred *cred = req->rq_cred;
831         struct ptlrpc_sec *sec;
832         struct ptlrpc_secops *ops;
833         int msg_payload, sec_payload;
834         ENTRY;
835
836         LASSERT(msgsize % 8 == 0);
837         LASSERT(sizeof(struct ptlrpcs_wire_hdr) % 8 == 0);
838         LASSERT(cred);
839         LASSERT(atomic_read(&cred->pc_refcount));
840         LASSERT(cred->pc_sec);
841         LASSERT(cred->pc_sec->ps_type);
842         LASSERT(cred->pc_sec->ps_type->pst_ops);
843         LASSERT(req->rq_repbuf == NULL);
844
845         sec = cred->pc_sec;
846         ops = sec->ps_type->pst_ops;
847         if (ops->alloc_repbuf)
848                 RETURN(ops->alloc_repbuf(sec, req, msgsize));
849
850         /* default allocation scheme */
851         msg_payload = sec->ps_sectype == PTLRPC_SEC_TYPE_PRIV ? 0 : msgsize;
852         sec_payload = size_round(ptlrpcs_est_rep_payload(sec, msgsize));
853
854         req->rq_repbuf_len = sizeof(struct ptlrpcs_wire_hdr) +
855                              msg_payload + sec_payload;
856         OBD_ALLOC(req->rq_repbuf, req->rq_repbuf_len);
857         if (!req->rq_repbuf)
858                 RETURN(-ENOMEM);
859
860         CDEBUG(D_SEC, "req %p: repbuf at %p, len %d, msg %d, sec %d\n",
861                req, req->rq_repbuf, req->rq_repbuf_len,
862                msg_payload, sec_payload);
863
864         RETURN(0);
865 }
866
867 void ptlrpcs_cli_free_repbuf(struct ptlrpc_request *req)
868 {
869         struct ptlrpc_cred *cred = req->rq_cred;
870         struct ptlrpc_sec *sec;
871         struct ptlrpc_secops *ops;
872         ENTRY;
873
874         LASSERT(cred);
875         LASSERT(atomic_read(&cred->pc_refcount));
876         LASSERT(cred->pc_sec);
877         LASSERT(cred->pc_sec->ps_type);
878         LASSERT(cred->pc_sec->ps_type->pst_ops);
879         LASSERT(req->rq_repbuf);
880
881         sec = cred->pc_sec;
882         ops = sec->ps_type->pst_ops;
883         if (ops->free_repbuf)
884                 ops->free_repbuf(sec, req);
885         else {
886                 OBD_FREE(req->rq_repbuf, req->rq_repbuf_len);
887                 req->rq_repbuf = NULL;
888                 req->rq_repmsg = NULL;
889         }
890         EXIT;
891 }
892
893 int ptlrpcs_import_get_sec(struct obd_import *imp)
894 {
895         ptlrpcs_flavor_t flavor = {PTLRPC_SEC_NULL, 0};
896         char *pipedir = NULL;
897         ENTRY;
898
899         LASSERT(imp->imp_obd);
900         LASSERT(imp->imp_obd->obd_type);
901
902         /* old sec might be still there in reconnecting */
903         if (imp->imp_sec)
904                 RETURN(0);
905
906         /* find actual flavor for client obd. right now server side
907          * obd (reverse imp, etc) will simply use NULL.
908          */
909         if (!strcmp(imp->imp_obd->obd_type->typ_name, "mdc") ||
910             !strcmp(imp->imp_obd->obd_type->typ_name, "osc")) {
911                 struct client_obd *cli = &imp->imp_obd->u.cli;
912
913                 if (cli->cl_sec_flavor == PTLRPC_SEC_GSS) {
914                         CWARN("select security gss/%s for %s(%s)\n",
915                                cli->cl_sec_subflavor == PTLRPC_SEC_GSS_KRB5I ?
916                                "krb5i" : "krb5p",
917                                imp->imp_obd->obd_type->typ_name,
918                                imp->imp_obd->obd_name);
919                         flavor.flavor = cli->cl_sec_flavor;
920                         flavor.subflavor = cli->cl_sec_subflavor;
921                         pipedir = imp->imp_obd->obd_name;
922                 } else if (cli->cl_sec_flavor == PTLRPC_SEC_NULL) {
923                         CWARN("select security null for %s(%s)\n",
924                                imp->imp_obd->obd_type->typ_name,
925                                imp->imp_obd->obd_name);
926                 } else {
927                         CWARN("unknown security flavor for mdc(%s), "
928                               "use 'null'\n", imp->imp_obd->obd_name);
929                 }
930         }
931
932         imp->imp_sec = ptlrpcs_sec_create(&flavor, imp, pipedir, imp);
933         if (!imp->imp_sec)
934                 RETURN(-EINVAL);
935         else
936                 RETURN(0);
937 }
938
939 void ptlrpcs_import_drop_sec(struct obd_import *imp)
940 {
941         ENTRY;
942         if (imp->imp_sec) {
943                 ptlrpcs_sec_put(imp->imp_sec);
944                 imp->imp_sec = NULL;
945         }
946         EXIT;
947 }
948
949 int __init ptlrpc_sec_init(void)
950 {
951         int rc;
952
953         if ((rc = ptlrpcs_null_init()))
954                 return rc;
955
956         if ((rc = svcsec_null_init())) {
957                 ptlrpcs_null_exit();
958                 return rc;
959         }
960
961 #if 0
962 #if !defined __KERNEL__ && defined ENABLE_GSS
963         ptlrpcs_gss_init();
964 #endif
965 #endif
966         return 0;
967 }
968
969 static void __exit ptlrpc_sec_exit(void)
970 {
971         svcsec_null_exit();
972         ptlrpcs_null_exit();
973 }
974
975
976 EXPORT_SYMBOL(ptlrpcs_register);
977 EXPORT_SYMBOL(ptlrpcs_unregister);
978 EXPORT_SYMBOL(ptlrpcs_sec_create);
979 EXPORT_SYMBOL(ptlrpcs_sec_put);
980 EXPORT_SYMBOL(ptlrpcs_sec_invalidate_cache);
981 EXPORT_SYMBOL(ptlrpcs_import_get_sec);
982 EXPORT_SYMBOL(ptlrpcs_import_drop_sec);
983 EXPORT_SYMBOL(ptlrpcs_cred_lookup);
984 EXPORT_SYMBOL(ptlrpcs_cred_put);
985 EXPORT_SYMBOL(ptlrpcs_req_get_cred);
986 EXPORT_SYMBOL(ptlrpcs_req_drop_cred);
987 EXPORT_SYMBOL(ptlrpcs_req_replace_dead_cred);
988 EXPORT_SYMBOL(ptlrpcs_req_refresh_cred);
989 EXPORT_SYMBOL(ptlrpcs_check_cred);
990 EXPORT_SYMBOL(ptlrpcs_cli_alloc_reqbuf);
991 EXPORT_SYMBOL(ptlrpcs_cli_free_reqbuf);
992 EXPORT_SYMBOL(ptlrpcs_cli_alloc_repbuf);
993 EXPORT_SYMBOL(ptlrpcs_cli_free_repbuf);
994 EXPORT_SYMBOL(ptlrpcs_cli_wrap_request);
995 EXPORT_SYMBOL(ptlrpcs_cli_unwrap_reply);
996 EXPORT_SYMBOL(sec_alloc_reqbuf);
997 EXPORT_SYMBOL(sec_free_reqbuf);
998
999 EXPORT_SYMBOL(svcsec_register);
1000 EXPORT_SYMBOL(svcsec_unregister);
1001 EXPORT_SYMBOL(svcsec_accept);
1002 EXPORT_SYMBOL(svcsec_authorize);
1003 EXPORT_SYMBOL(svcsec_alloc_repbuf);
1004 EXPORT_SYMBOL(svcsec_cleanup_req);
1005 EXPORT_SYMBOL(svcsec_get);
1006 EXPORT_SYMBOL(svcsec_put);
1007 EXPORT_SYMBOL(svcsec_alloc_reply_state);
1008 EXPORT_SYMBOL(svcsec_free_reply_state);
1009
1010 MODULE_AUTHOR("Cluster File Systems, Inc. <info@clusterfs.com>");
1011 MODULE_DESCRIPTION("Lustre Security Support");
1012 MODULE_LICENSE("GPL");
1013
1014 module_init(ptlrpc_sec_init);
1015 module_exit(ptlrpc_sec_exit);