Whamcloud - gitweb
sometime we don't need gracefully flush client credential.
[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 /*
213  * grace: mark cred DEAD, allow graceful destroy like notify
214  *        server side, etc.
215  * force: flush all entries, otherwise only free ones be flushed.
216  */
217 static
218 int ptlrpcs_flush_credcache(struct ptlrpc_sec *sec, int grace, int force)
219 {
220         struct ptlrpc_cred *cred, *n;
221         LIST_HEAD(freelist);
222         int i, busy = 0;
223         ENTRY;
224
225         spin_lock(&sec->ps_lock);
226         for (i = 0; i < PTLRPC_CREDCACHE_NR; i++) {
227                 list_for_each_entry_safe(cred, n, &sec->ps_credcache[i],
228                                          pc_hash) {
229                         LASSERT(atomic_read(&cred->pc_refcount) >= 0);
230                         if (atomic_read(&cred->pc_refcount)) {
231                                 busy = 1;
232                                 if (!force)
233                                         continue;
234                                 list_del_init(&cred->pc_hash);
235                         } else
236                                 list_move(&cred->pc_hash, &freelist);
237
238                         cred->pc_flags |= PTLRPC_CRED_DEAD;
239                         if (!grace)
240                                 cred->pc_flags &= ~PTLRPC_CRED_UPTODATE;
241                 }
242         }
243         spin_unlock(&sec->ps_lock);
244         ptlrpcs_destroy_credlist(&freelist);
245         RETURN(busy);
246 }
247
248 /**************************************************
249  * credential APIs                                *
250  **************************************************/
251
252 static inline
253 int ptlrpcs_cred_get_hash(__u64 pag)
254 {
255         LASSERT((pag & PTLRPC_CREDCACHE_MASK) < PTLRPC_CREDCACHE_NR);
256         return (pag & PTLRPC_CREDCACHE_MASK);
257 }
258
259 static
260 struct ptlrpc_cred * cred_cache_lookup(struct ptlrpc_sec *sec,
261                                        struct vfs_cred *vcred,
262                                        int create)
263 {
264         struct ptlrpc_cred *cred, *new = NULL, *n;
265         LIST_HEAD(freelist);
266         int hash, found = 0;
267         ENTRY;
268
269         hash = ptlrpcs_cred_get_hash(vcred->vc_pag);
270
271 retry:
272         spin_lock(&sec->ps_lock);
273         /* do gc if expired */
274         if (time_after(get_seconds(), sec->ps_nextgc))
275                 ptlrpcs_credcache_gc(sec, &freelist);
276
277         list_for_each_entry_safe(cred, n, &sec->ps_credcache[hash], pc_hash) {
278                 /* for DEAD and ERROR entries, its final put will
279                  * release them, so we simply skip here.
280                  */
281                 if (cred->pc_flags & (PTLRPC_CRED_DEAD | PTLRPC_CRED_ERROR)) {
282                         LASSERT(atomic_read(&cred->pc_refcount));
283                         continue;
284                 }
285                 if (ptlrpcs_cred_unlink_expired(cred, &freelist))
286                         continue;
287                 if (cred->pc_ops->match(cred, vcred)) {
288                         found = 1;
289                         break;
290                 }
291         }
292
293         if (found) {
294                 if (new && new != cred) {
295                         /* lost the race, just free it */
296                         list_add(&new->pc_hash, &freelist);
297                 }
298                 list_move(&cred->pc_hash, &sec->ps_credcache[hash]);
299         } else {
300                 if (new) {
301                         list_add(&new->pc_hash, &sec->ps_credcache[hash]);
302                         cred = new;
303                 } else if (create) {
304                         spin_unlock(&sec->ps_lock);
305                         new = sec->ps_type->pst_ops->create_cred(sec, vcred);
306                         if (new) {
307                                 atomic_inc(&sec->ps_credcount);
308                                 goto retry;
309                         }
310                 } else
311                         cred = NULL;
312         }
313
314         /* hold a ref */
315         if (cred)
316                 atomic_inc(&cred->pc_refcount);
317
318         spin_unlock(&sec->ps_lock);
319
320         ptlrpcs_destroy_credlist(&freelist);
321         RETURN(cred);
322 }
323
324 struct ptlrpc_cred * ptlrpcs_cred_lookup(struct ptlrpc_sec *sec,
325                                          struct vfs_cred *vcred)
326 {
327         struct ptlrpc_cred *cred;
328         ENTRY;
329
330         cred = cred_cache_lookup(sec, vcred, 0);
331         RETURN(cred);
332 }
333
334 static struct ptlrpc_cred *get_cred(struct ptlrpc_sec *sec)
335 {
336         struct vfs_cred vcred;
337
338         LASSERT(sec);
339         /* XXX
340          * for now we simply let PAG == real uid
341          */
342         vcred.vc_pag = (__u64) current->uid;
343         vcred.vc_uid = current->uid;
344
345         return cred_cache_lookup(sec, &vcred, 1);
346 }
347
348 int ptlrpcs_req_get_cred(struct ptlrpc_request *req)
349 {
350         struct obd_import *imp = req->rq_import;
351         ENTRY;
352
353         LASSERT(!req->rq_cred);
354         LASSERT(imp);
355
356         req->rq_cred = get_cred(imp->imp_sec);
357
358         if (!req->rq_cred) {
359                 CERROR("req %p: fail to get cred from cache\n", req);
360                 RETURN(-ENOMEM);
361         }
362
363         RETURN(0);
364 }
365
366 /*
367  * check whether current user have valid credential for an import or not.
368  * might repeatedly try in case of non-fatal errors.
369  * return 0 on success, 1 on failure
370  */
371 int ptlrpcs_check_cred(struct obd_import *imp)
372 {
373         struct ptlrpc_cred *cred;
374         ENTRY;
375
376 again:
377         cred = get_cred(imp->imp_sec);
378         if (!cred)
379                 RETURN(0);
380
381         if (ptlrpcs_cred_is_uptodate(cred)) {
382                 if (!ptlrpcs_cred_check_expire(cred)) {
383                         ptlrpcs_cred_put(cred, 1);
384                         RETURN(0);
385                 } else {
386                         ptlrpcs_cred_put(cred, 1);
387                         goto again;
388                 }
389         }
390
391         ptlrpcs_cred_refresh(cred);
392         if (ptlrpcs_cred_is_uptodate(cred)) {
393                 ptlrpcs_cred_put(cred, 1);
394                 RETURN(0);
395         }
396
397         if (cred->pc_flags & PTLRPC_CRED_ERROR ||
398             !imp->imp_replayable) {
399                 ptlrpcs_cred_put(cred, 1);
400                 RETURN(1);
401         }
402
403         ptlrpcs_cred_put(cred, 1);
404
405         if (signal_pending(current)) {
406                 CWARN("%s: interrupted\n", current->comm);
407                 RETURN(1);
408         }
409         goto again;
410 }
411
412 static void ptlrpcs_sec_destroy(struct ptlrpc_sec *sec);
413
414 void ptlrpcs_cred_put(struct ptlrpc_cred *cred, int sync)
415 {
416         struct ptlrpc_sec *sec = cred->pc_sec;
417
418         LASSERT(cred);
419         LASSERT(sec);
420         LASSERT(atomic_read(&cred->pc_refcount));
421
422         spin_lock(&sec->ps_lock);
423         if (atomic_dec_and_test(&cred->pc_refcount) && sync &&
424             cred->pc_flags & (PTLRPC_CRED_DEAD | PTLRPC_CRED_ERROR)) {
425                 list_del_init(&cred->pc_hash);
426                 ptlrpcs_cred_destroy(cred);
427                 if (!atomic_read(&sec->ps_credcount) &&
428                     !atomic_read(&sec->ps_refcount)) {
429                         CWARN("put last cred on a dead sec %p(%s), "
430                               "also destroy the sec\n", sec,
431                                sec->ps_type->pst_name);
432                         spin_unlock(&sec->ps_lock);
433
434                         ptlrpcs_sec_destroy(sec);
435                         return;
436                 }
437         }
438         spin_unlock(&sec->ps_lock);
439 }
440
441 void ptlrpcs_req_drop_cred(struct ptlrpc_request *req)
442 {
443         ENTRY;
444
445         LASSERT(req);
446         LASSERT(req->rq_cred);
447
448         if (req->rq_cred) {
449                 /* We'd like to not use 'sync' mode, but might cause
450                  * some cred leak. Need more thinking here. FIXME
451                  */
452                 ptlrpcs_cred_put(req->rq_cred, 1);
453                 req->rq_cred = NULL;
454         } else
455                 CDEBUG(D_SEC, "req %p have no cred\n", req);
456         EXIT;
457 }
458
459 /* 
460  * request must have a cred. if failed to get new cred,
461  * just restore the old one
462  */
463 int ptlrpcs_req_replace_dead_cred(struct ptlrpc_request *req)
464 {
465         struct ptlrpc_cred *cred = req->rq_cred;
466         int rc;
467         ENTRY;
468
469         LASSERT(cred);
470         LASSERT(cred->pc_flags & PTLRPC_CRED_DEAD);
471
472         ptlrpcs_cred_get(cred);
473         ptlrpcs_req_drop_cred(req);
474         LASSERT(!req->rq_cred);
475         rc = ptlrpcs_req_get_cred(req);
476         if (!rc) {
477                 LASSERT(req->rq_cred);
478                 LASSERT(req->rq_cred != cred);
479                 ptlrpcs_cred_put(cred, 1);
480         } else {
481                 LASSERT(!req->rq_cred);
482                 req->rq_cred = cred;
483         }
484         RETURN(rc);
485 }
486
487 /*
488  * since there's no lock on the cred, its status could be changed
489  * by other threads at any time, we allow this race.
490  */
491 int ptlrpcs_req_refresh_cred(struct ptlrpc_request *req)
492 {
493         struct ptlrpc_cred *cred = req->rq_cred;
494         ENTRY;
495
496         LASSERT(cred);
497
498         if (ptlrpcs_cred_is_uptodate(cred)) {
499                 if (!ptlrpcs_cred_check_expire(cred))
500                         RETURN(0);
501         }
502
503         if (cred->pc_flags & PTLRPC_CRED_ERROR) {
504                 req->rq_ptlrpcs_err = 1;
505                 RETURN(-EPERM);
506         }
507
508         if (cred->pc_flags & PTLRPC_CRED_DEAD) {
509                 if (ptlrpcs_req_replace_dead_cred(req) == 0) {
510                         LASSERT(cred != req->rq_cred);
511                         CDEBUG(D_SEC, "req %p: replace cred %p => %p\n",
512                                req, cred, req->rq_cred);
513                         cred = req->rq_cred;
514                 } else {
515                         LASSERT(cred == req->rq_cred);
516                         CERROR("req %p: failed to replace dead cred %p\n",
517                                 req, cred);
518                         req->rq_ptlrpcs_err = 1;
519                         RETURN(-ENOMEM);
520                 }
521         }
522
523         ptlrpcs_cred_refresh(cred);
524         if (!ptlrpcs_cred_is_uptodate(cred)) {
525                 if (cred->pc_flags & PTLRPC_CRED_ERROR)
526                         req->rq_ptlrpcs_err = 1;
527
528                 CERROR("req %p: failed to refresh cred %p, fatal %d\n",
529                         req, cred, req->rq_ptlrpcs_err);
530                 RETURN(-EPERM);
531         } else
532                 RETURN(0);
533 }
534
535 int ptlrpcs_cli_wrap_request(struct ptlrpc_request *req)
536 {
537         struct ptlrpc_cred     *cred;
538         int rc;
539         ENTRY;
540
541         LASSERT(req->rq_cred);
542         LASSERT(req->rq_cred->pc_sec);
543         LASSERT(req->rq_cred->pc_ops);
544         LASSERT(req->rq_reqbuf);
545         LASSERT(req->rq_reqbuf_len);
546
547         rc = ptlrpcs_req_refresh_cred(req);
548         if (rc)
549                 RETURN(rc);
550
551         CDEBUG(D_SEC, "wrap req %p\n", req);
552         cred = req->rq_cred;
553
554         switch (cred->pc_sec->ps_sectype) {
555         case PTLRPC_SEC_TYPE_NONE:
556         case PTLRPC_SEC_TYPE_AUTH:
557                 if (req->rq_req_wrapped) {
558                         CDEBUG(D_SEC, "req %p(o%u,x"LPU64",t"LPU64") "
559                                "already signed, resend?\n", req,
560                                req->rq_reqmsg ? req->rq_reqmsg->opc : -1,
561                                req->rq_xid, req->rq_transno);
562                         req->rq_req_wrapped = 0;
563                         req->rq_reqdata_len = sizeof(struct ptlrpcs_wire_hdr) +
564                                               req->rq_reqlen;
565                         LASSERT(req->rq_reqdata_len % 8 == 0);
566                 }
567
568                 LASSERT(cred->pc_ops->sign);
569                 rc = cred->pc_ops->sign(cred, req);
570                 if (!rc)
571                         req->rq_req_wrapped = 1;
572                 break;
573         case PTLRPC_SEC_TYPE_PRIV:
574                 if (req->rq_req_wrapped) {
575                         CDEBUG(D_SEC, "req %p(o%u,x"LPU64",t"LPU64") "
576                                "already encrypted, resend?\n", req,
577                                req->rq_reqmsg ? req->rq_reqmsg->opc : -1,
578                                req->rq_xid, req->rq_transno);
579                         req->rq_req_wrapped = 0;
580                         req->rq_reqdata_len = sizeof(struct ptlrpcs_wire_hdr);
581                         LASSERT(req->rq_reqdata_len % 8 == 0);
582                 }
583
584                 LASSERT(cred->pc_ops->seal);
585                 rc = cred->pc_ops->seal(cred, req);
586                 if (!rc)
587                         req->rq_req_wrapped = 1;
588                 break;
589         default:
590                 LBUG();
591         }
592         LASSERT(req->rq_reqdata_len);
593         LASSERT(req->rq_reqdata_len % 8 == 0);
594         LASSERT(req->rq_reqdata_len >= sizeof(struct ptlrpcs_wire_hdr));
595         LASSERT(req->rq_reqdata_len <= req->rq_reqbuf_len);
596
597         RETURN(rc);
598 }
599
600 /* rq_nob_received is the actual received data length */
601 int ptlrpcs_cli_unwrap_reply(struct ptlrpc_request *req)
602 {
603         struct ptlrpc_cred *cred = req->rq_cred;
604         struct ptlrpc_sec *sec;
605         struct ptlrpcs_wire_hdr *sec_hdr;
606         int rc;
607         ENTRY;
608
609         LASSERT(cred);
610         LASSERT(cred->pc_sec);
611         LASSERT(cred->pc_ops);
612         LASSERT(req->rq_repbuf);
613         
614         if (req->rq_nob_received < sizeof(*sec_hdr)) {
615                 CERROR("req %p: reply size only %d\n",
616                         req, req->rq_nob_received);
617                 RETURN(-EPROTO);
618         }
619
620         sec_hdr = (struct ptlrpcs_wire_hdr *) req->rq_repbuf;
621         sec_hdr->flavor = le32_to_cpu(sec_hdr->flavor);
622         sec_hdr->sectype = le32_to_cpu(sec_hdr->sectype);
623         sec_hdr->msg_len = le32_to_cpu(sec_hdr->msg_len);
624         sec_hdr->sec_len = le32_to_cpu(sec_hdr->sec_len);
625
626         CDEBUG(D_SEC, "req %p, cred %p, flavor %u, sectype %u\n",
627                req, cred, sec_hdr->flavor, sec_hdr->sectype);
628
629         sec = cred->pc_sec;
630         if (sec_hdr->flavor != sec->ps_flavor.flavor) {
631                 CERROR("unmatched flavor %u while expect %u\n",
632                        sec_hdr->flavor, sec->ps_flavor.flavor);
633                 RETURN(-EPROTO);
634         }
635
636         if (sizeof(*sec_hdr) + sec_hdr->msg_len + sec_hdr->sec_len >
637             req->rq_nob_received) {
638                 CERROR("msg %u, sec %u, while only get %d\n",
639                         sec_hdr->msg_len, sec_hdr->sec_len,
640                         req->rq_nob_received);
641                 RETURN(-EPROTO);
642         }
643
644         switch (sec_hdr->sectype) {
645         case PTLRPC_SEC_TYPE_NONE:
646         case PTLRPC_SEC_TYPE_AUTH: {
647                 LASSERT(cred->pc_ops->verify);
648                 rc = cred->pc_ops->verify(cred, req);
649                 LASSERT(rc || req->rq_repmsg || req->rq_ptlrpcs_restart);
650                 break;
651         case PTLRPC_SEC_TYPE_PRIV:
652                 LASSERT(cred->pc_ops->unseal);
653                 rc = cred->pc_ops->unseal(cred, req);
654                 LASSERT(rc || req->rq_repmsg || req->rq_ptlrpcs_restart);
655                 break;
656         }
657         default:
658                 rc = -1;
659                 LBUG();
660         }
661         RETURN(rc);
662 }
663
664 /**************************************************
665  * security APIs                                  *
666  **************************************************/
667
668 struct ptlrpc_sec * ptlrpcs_sec_create(ptlrpcs_flavor_t *flavor,
669                                        struct obd_import *import,
670                                        const char *pipe_dir,
671                                        void *pipe_data)
672 {
673         struct ptlrpc_sec_type *type;
674         struct ptlrpc_sec *sec;
675         ENTRY;
676
677         type = ptlrpcs_flavor2type(flavor);
678         if (!type) {
679                 CDEBUG(D_SEC, "invalid major flavor %u\n", flavor->flavor);
680                 RETURN(NULL);
681         }
682
683         sec = type->pst_ops->create_sec(flavor, pipe_dir, pipe_data);
684         if (sec) {
685                 spin_lock_init(&sec->ps_lock);
686                 ptlrpcs_init_credcache(sec);
687                 sec->ps_type = type;
688                 sec->ps_flavor = *flavor;
689                 sec->ps_import = class_import_get(import);
690                 atomic_set(&sec->ps_refcount, 1);
691                 atomic_set(&sec->ps_credcount, 0);
692                 atomic_inc(&type->pst_inst);
693         } else
694                 ptlrpcs_type_put(type);
695
696         return sec;
697 }
698
699 static void ptlrpcs_sec_destroy(struct ptlrpc_sec *sec)
700 {
701         struct ptlrpc_sec_type *type = sec->ps_type;
702         struct obd_import *imp = sec->ps_import;
703
704         LASSERT(type && type->pst_ops);
705         LASSERT(type->pst_ops->destroy_sec);
706
707         type->pst_ops->destroy_sec(sec);
708         atomic_dec(&type->pst_inst);
709         ptlrpcs_type_put(type);
710         class_import_put(imp);
711 }
712
713 void ptlrpcs_sec_put(struct ptlrpc_sec *sec)
714 {
715         if (atomic_dec_and_test(&sec->ps_refcount)) {
716                 ptlrpcs_flush_credcache(sec, 1, 1);
717
718                 if (atomic_read(&sec->ps_credcount) == 0) {
719                         ptlrpcs_sec_destroy(sec);
720                 } else {
721                         CWARN("sec %p(%s) is no usage while %d cred still "
722                               "holded, destroy delayed\n",
723                                sec, sec->ps_type->pst_name,
724                                atomic_read(&sec->ps_credcount));
725                 }
726         }
727 }
728
729 void ptlrpcs_sec_invalidate_cache(struct ptlrpc_sec *sec)
730 {
731         ptlrpcs_flush_credcache(sec, 0, 1);
732 }
733
734 int sec_alloc_reqbuf(struct ptlrpc_sec *sec,
735                      struct ptlrpc_request *req,
736                      int msgsize, int secsize)
737 {
738         struct ptlrpcs_wire_hdr *hdr;
739         ENTRY;
740
741         LASSERT(msgsize % 8 == 0);
742         LASSERT(secsize % 8 == 0);
743
744         req->rq_reqbuf_len = sizeof(*hdr) + msgsize + secsize;
745         OBD_ALLOC(req->rq_reqbuf, req->rq_reqbuf_len);
746         if (!req->rq_reqbuf) {
747                 CERROR("can't alloc %d\n", req->rq_reqbuf_len);
748                 RETURN(-ENOMEM);
749         }
750
751         hdr = buf_to_sec_hdr(req->rq_reqbuf);
752         hdr->flavor = cpu_to_le32(sec->ps_flavor.flavor);
753         hdr->sectype = cpu_to_le32(sec->ps_sectype);
754         hdr->msg_len = msgsize;
755         /* security length will be filled later */
756
757         /* later reqdata_len will be added on actual security payload */
758         req->rq_reqdata_len = sizeof(*hdr) + msgsize;
759         req->rq_reqmsg = buf_to_lustre_msg(req->rq_reqbuf);
760
761         CDEBUG(D_SEC, "req %p: rqbuf at %p, len %d, msg %d, sec %d\n",
762                req, req->rq_reqbuf, req->rq_reqbuf_len,
763                msgsize, secsize);
764
765         RETURN(0);
766 }
767
768 /* when complete successfully, req->rq_reqmsg should point to the
769  * right place.
770  */
771 int ptlrpcs_cli_alloc_reqbuf(struct ptlrpc_request *req, int msgsize)
772 {
773         struct ptlrpc_cred *cred = req->rq_cred;
774         struct ptlrpc_sec *sec;
775         struct ptlrpc_secops *ops;
776
777         LASSERT(msgsize % 8 == 0);
778         LASSERT(sizeof(struct ptlrpcs_wire_hdr) % 8 == 0);
779         LASSERT(cred);
780         LASSERT(atomic_read(&cred->pc_refcount));
781         LASSERT(cred->pc_sec);
782         LASSERT(cred->pc_sec->ps_type);
783         LASSERT(cred->pc_sec->ps_type->pst_ops);
784         LASSERT(req->rq_reqbuf == NULL);
785         LASSERT(req->rq_reqmsg == NULL);
786
787         sec = cred->pc_sec;
788         ops = sec->ps_type->pst_ops;
789         if (ops->alloc_reqbuf)
790                 return ops->alloc_reqbuf(sec, req, msgsize);
791         else
792                 return sec_alloc_reqbuf(sec, req, msgsize, 0);
793 }
794
795 void sec_free_reqbuf(struct ptlrpc_sec *sec,
796                      struct ptlrpc_request *req)
797 {
798         LASSERT(req->rq_reqbuf);
799         LASSERT(req->rq_reqbuf_len);
800
801         /* sanity check */
802         if (req->rq_reqmsg) {
803                 LASSERT((char *) req->rq_reqmsg >= req->rq_reqbuf &&
804                         (char *) req->rq_reqmsg < req->rq_reqbuf +
805                                                   req->rq_reqbuf_len);
806         }
807
808         OBD_FREE(req->rq_reqbuf, req->rq_reqbuf_len);
809         req->rq_reqbuf = NULL;
810         req->rq_reqmsg = NULL;
811 }
812
813 void ptlrpcs_cli_free_reqbuf(struct ptlrpc_request *req)
814 {
815         struct ptlrpc_cred *cred = req->rq_cred;
816         struct ptlrpc_sec *sec;
817         struct ptlrpc_secops *ops;
818
819         LASSERT(cred);
820         LASSERT(atomic_read(&cred->pc_refcount));
821         LASSERT(cred->pc_sec);
822         LASSERT(cred->pc_sec->ps_type);
823         LASSERT(cred->pc_sec->ps_type->pst_ops);
824         LASSERT(req->rq_reqbuf);
825
826         sec = cred->pc_sec;
827         ops = sec->ps_type->pst_ops;
828         if (ops->free_reqbuf)
829                 ops->free_reqbuf(sec, req);
830         else
831                 sec_free_reqbuf(sec, req);
832 }
833
834 int ptlrpcs_cli_alloc_repbuf(struct ptlrpc_request *req, int msgsize)
835 {
836         struct ptlrpc_cred *cred = req->rq_cred;
837         struct ptlrpc_sec *sec;
838         struct ptlrpc_secops *ops;
839         int msg_payload, sec_payload;
840         ENTRY;
841
842         LASSERT(msgsize % 8 == 0);
843         LASSERT(sizeof(struct ptlrpcs_wire_hdr) % 8 == 0);
844         LASSERT(cred);
845         LASSERT(atomic_read(&cred->pc_refcount));
846         LASSERT(cred->pc_sec);
847         LASSERT(cred->pc_sec->ps_type);
848         LASSERT(cred->pc_sec->ps_type->pst_ops);
849         LASSERT(req->rq_repbuf == NULL);
850
851         sec = cred->pc_sec;
852         ops = sec->ps_type->pst_ops;
853         if (ops->alloc_repbuf)
854                 RETURN(ops->alloc_repbuf(sec, req, msgsize));
855
856         /* default allocation scheme */
857         msg_payload = sec->ps_sectype == PTLRPC_SEC_TYPE_PRIV ? 0 : msgsize;
858         sec_payload = size_round(ptlrpcs_est_rep_payload(sec, msgsize));
859
860         req->rq_repbuf_len = sizeof(struct ptlrpcs_wire_hdr) +
861                              msg_payload + sec_payload;
862         OBD_ALLOC(req->rq_repbuf, req->rq_repbuf_len);
863         if (!req->rq_repbuf)
864                 RETURN(-ENOMEM);
865
866         CDEBUG(D_SEC, "req %p: repbuf at %p, len %d, msg %d, sec %d\n",
867                req, req->rq_repbuf, req->rq_repbuf_len,
868                msg_payload, sec_payload);
869
870         RETURN(0);
871 }
872
873 void ptlrpcs_cli_free_repbuf(struct ptlrpc_request *req)
874 {
875         struct ptlrpc_cred *cred = req->rq_cred;
876         struct ptlrpc_sec *sec;
877         struct ptlrpc_secops *ops;
878         ENTRY;
879
880         LASSERT(cred);
881         LASSERT(atomic_read(&cred->pc_refcount));
882         LASSERT(cred->pc_sec);
883         LASSERT(cred->pc_sec->ps_type);
884         LASSERT(cred->pc_sec->ps_type->pst_ops);
885         LASSERT(req->rq_repbuf);
886
887         sec = cred->pc_sec;
888         ops = sec->ps_type->pst_ops;
889         if (ops->free_repbuf)
890                 ops->free_repbuf(sec, req);
891         else {
892                 OBD_FREE(req->rq_repbuf, req->rq_repbuf_len);
893                 req->rq_repbuf = NULL;
894                 req->rq_repmsg = NULL;
895         }
896         EXIT;
897 }
898
899 int ptlrpcs_import_get_sec(struct obd_import *imp)
900 {
901         ptlrpcs_flavor_t flavor = {PTLRPC_SEC_NULL, 0};
902         char *pipedir = NULL;
903         ENTRY;
904
905         LASSERT(imp->imp_obd);
906         LASSERT(imp->imp_obd->obd_type);
907
908         /* old sec might be still there in reconnecting */
909         if (imp->imp_sec)
910                 RETURN(0);
911
912         /* find actual flavor for client obd. right now server side
913          * obd (reverse imp, etc) will simply use NULL.
914          */
915         if (!strcmp(imp->imp_obd->obd_type->typ_name, "mdc") ||
916             !strcmp(imp->imp_obd->obd_type->typ_name, "osc")) {
917                 struct client_obd *cli = &imp->imp_obd->u.cli;
918
919                 if (cli->cl_sec_flavor == PTLRPC_SEC_GSS) {
920                         CWARN("select security gss/%s for %s(%s)\n",
921                                cli->cl_sec_subflavor == PTLRPC_SEC_GSS_KRB5I ?
922                                "krb5i" : "krb5p",
923                                imp->imp_obd->obd_type->typ_name,
924                                imp->imp_obd->obd_name);
925                         flavor.flavor = cli->cl_sec_flavor;
926                         flavor.subflavor = cli->cl_sec_subflavor;
927                         pipedir = imp->imp_obd->obd_name;
928                 } else if (cli->cl_sec_flavor == PTLRPC_SEC_NULL) {
929                         CWARN("select security null for %s(%s)\n",
930                                imp->imp_obd->obd_type->typ_name,
931                                imp->imp_obd->obd_name);
932                 } else {
933                         CWARN("unknown security flavor for mdc(%s), "
934                               "use 'null'\n", imp->imp_obd->obd_name);
935                 }
936         }
937
938         imp->imp_sec = ptlrpcs_sec_create(&flavor, imp, pipedir, imp);
939         if (!imp->imp_sec)
940                 RETURN(-EINVAL);
941         else
942                 RETURN(0);
943 }
944
945 void ptlrpcs_import_drop_sec(struct obd_import *imp)
946 {
947         ENTRY;
948         if (imp->imp_sec) {
949                 ptlrpcs_sec_put(imp->imp_sec);
950                 imp->imp_sec = NULL;
951         }
952         EXIT;
953 }
954
955 int __init ptlrpc_sec_init(void)
956 {
957         int rc;
958
959         if ((rc = ptlrpcs_null_init()))
960                 return rc;
961
962         if ((rc = svcsec_null_init())) {
963                 ptlrpcs_null_exit();
964                 return rc;
965         }
966
967 #if 0
968 #if !defined __KERNEL__ && defined ENABLE_GSS
969         ptlrpcs_gss_init();
970 #endif
971 #endif
972         return 0;
973 }
974
975 static void __exit ptlrpc_sec_exit(void)
976 {
977         svcsec_null_exit();
978         ptlrpcs_null_exit();
979 }
980
981
982 EXPORT_SYMBOL(ptlrpcs_register);
983 EXPORT_SYMBOL(ptlrpcs_unregister);
984 EXPORT_SYMBOL(ptlrpcs_sec_create);
985 EXPORT_SYMBOL(ptlrpcs_sec_put);
986 EXPORT_SYMBOL(ptlrpcs_sec_invalidate_cache);
987 EXPORT_SYMBOL(ptlrpcs_import_get_sec);
988 EXPORT_SYMBOL(ptlrpcs_import_drop_sec);
989 EXPORT_SYMBOL(ptlrpcs_cred_lookup);
990 EXPORT_SYMBOL(ptlrpcs_cred_put);
991 EXPORT_SYMBOL(ptlrpcs_req_get_cred);
992 EXPORT_SYMBOL(ptlrpcs_req_drop_cred);
993 EXPORT_SYMBOL(ptlrpcs_req_replace_dead_cred);
994 EXPORT_SYMBOL(ptlrpcs_req_refresh_cred);
995 EXPORT_SYMBOL(ptlrpcs_check_cred);
996 EXPORT_SYMBOL(ptlrpcs_cli_alloc_reqbuf);
997 EXPORT_SYMBOL(ptlrpcs_cli_free_reqbuf);
998 EXPORT_SYMBOL(ptlrpcs_cli_alloc_repbuf);
999 EXPORT_SYMBOL(ptlrpcs_cli_free_repbuf);
1000 EXPORT_SYMBOL(ptlrpcs_cli_wrap_request);
1001 EXPORT_SYMBOL(ptlrpcs_cli_unwrap_reply);
1002 EXPORT_SYMBOL(sec_alloc_reqbuf);
1003 EXPORT_SYMBOL(sec_free_reqbuf);
1004
1005 EXPORT_SYMBOL(svcsec_register);
1006 EXPORT_SYMBOL(svcsec_unregister);
1007 EXPORT_SYMBOL(svcsec_accept);
1008 EXPORT_SYMBOL(svcsec_authorize);
1009 EXPORT_SYMBOL(svcsec_alloc_repbuf);
1010 EXPORT_SYMBOL(svcsec_cleanup_req);
1011 EXPORT_SYMBOL(svcsec_get);
1012 EXPORT_SYMBOL(svcsec_put);
1013 EXPORT_SYMBOL(svcsec_alloc_reply_state);
1014 EXPORT_SYMBOL(svcsec_free_reply_state);
1015
1016 MODULE_AUTHOR("Cluster File Systems, Inc. <info@clusterfs.com>");
1017 MODULE_DESCRIPTION("Lustre Security Support");
1018 MODULE_LICENSE("GPL");
1019
1020 module_init(ptlrpc_sec_init);
1021 module_exit(ptlrpc_sec_exit);