ALSA: opl4 - Fix a wrong argument in proc write callback
[safe/jmp/linux-2.6] / fs / ceph / auth_x.c
1
2 #include "ceph_debug.h"
3
4 #include <linux/err.h>
5 #include <linux/module.h>
6 #include <linux/random.h>
7 #include <linux/slab.h>
8
9 #include "auth_x.h"
10 #include "auth_x_protocol.h"
11 #include "crypto.h"
12 #include "auth.h"
13 #include "decode.h"
14
15 struct kmem_cache *ceph_x_ticketbuf_cachep;
16
17 #define TEMP_TICKET_BUF_LEN     256
18
19 static void ceph_x_validate_tickets(struct ceph_auth_client *ac, int *pneed);
20
21 static int ceph_x_is_authenticated(struct ceph_auth_client *ac)
22 {
23         struct ceph_x_info *xi = ac->private;
24         int need;
25
26         ceph_x_validate_tickets(ac, &need);
27         dout("ceph_x_is_authenticated want=%d need=%d have=%d\n",
28              ac->want_keys, need, xi->have_keys);
29         return (ac->want_keys & xi->have_keys) == ac->want_keys;
30 }
31
32 static int ceph_x_encrypt_buflen(int ilen)
33 {
34         return sizeof(struct ceph_x_encrypt_header) + ilen + 16 +
35                 sizeof(u32);
36 }
37
38 static int ceph_x_encrypt(struct ceph_crypto_key *secret,
39                           void *ibuf, int ilen, void *obuf, size_t olen)
40 {
41         struct ceph_x_encrypt_header head = {
42                 .struct_v = 1,
43                 .magic = cpu_to_le64(CEPHX_ENC_MAGIC)
44         };
45         size_t len = olen - sizeof(u32);
46         int ret;
47
48         ret = ceph_encrypt2(secret, obuf + sizeof(u32), &len,
49                             &head, sizeof(head), ibuf, ilen);
50         if (ret)
51                 return ret;
52         ceph_encode_32(&obuf, len);
53         return len + sizeof(u32);
54 }
55
56 static int ceph_x_decrypt(struct ceph_crypto_key *secret,
57                           void **p, void *end, void *obuf, size_t olen)
58 {
59         struct ceph_x_encrypt_header head;
60         size_t head_len = sizeof(head);
61         int len, ret;
62
63         len = ceph_decode_32(p);
64         if (*p + len > end)
65                 return -EINVAL;
66
67         dout("ceph_x_decrypt len %d\n", len);
68         ret = ceph_decrypt2(secret, &head, &head_len, obuf, &olen,
69                             *p, len);
70         if (ret)
71                 return ret;
72         if (head.struct_v != 1 || le64_to_cpu(head.magic) != CEPHX_ENC_MAGIC)
73                 return -EPERM;
74         *p += len;
75         return olen;
76 }
77
78 /*
79  * get existing (or insert new) ticket handler
80  */
81 struct ceph_x_ticket_handler *get_ticket_handler(struct ceph_auth_client *ac,
82                                                  int service)
83 {
84         struct ceph_x_ticket_handler *th;
85         struct ceph_x_info *xi = ac->private;
86         struct rb_node *parent = NULL, **p = &xi->ticket_handlers.rb_node;
87
88         while (*p) {
89                 parent = *p;
90                 th = rb_entry(parent, struct ceph_x_ticket_handler, node);
91                 if (service < th->service)
92                         p = &(*p)->rb_left;
93                 else if (service > th->service)
94                         p = &(*p)->rb_right;
95                 else
96                         return th;
97         }
98
99         /* add it */
100         th = kzalloc(sizeof(*th), GFP_NOFS);
101         if (!th)
102                 return ERR_PTR(-ENOMEM);
103         th->service = service;
104         rb_link_node(&th->node, parent, p);
105         rb_insert_color(&th->node, &xi->ticket_handlers);
106         return th;
107 }
108
109 static void remove_ticket_handler(struct ceph_auth_client *ac,
110                                   struct ceph_x_ticket_handler *th)
111 {
112         struct ceph_x_info *xi = ac->private;
113
114         dout("remove_ticket_handler %p %d\n", th, th->service);
115         rb_erase(&th->node, &xi->ticket_handlers);
116         ceph_crypto_key_destroy(&th->session_key);
117         if (th->ticket_blob)
118                 ceph_buffer_put(th->ticket_blob);
119         kfree(th);
120 }
121
122 static int ceph_x_proc_ticket_reply(struct ceph_auth_client *ac,
123                                     struct ceph_crypto_key *secret,
124                                     void *buf, void *end)
125 {
126         struct ceph_x_info *xi = ac->private;
127         int num;
128         void *p = buf;
129         int ret;
130         char *dbuf;
131         char *ticket_buf;
132         u8 struct_v;
133
134         dbuf = kmem_cache_alloc(ceph_x_ticketbuf_cachep, GFP_NOFS | GFP_ATOMIC);
135         if (!dbuf)
136                 return -ENOMEM;
137
138         ret = -ENOMEM;
139         ticket_buf = kmem_cache_alloc(ceph_x_ticketbuf_cachep,
140                                       GFP_NOFS | GFP_ATOMIC);
141         if (!ticket_buf)
142                 goto out_dbuf;
143
144         ceph_decode_need(&p, end, 1 + sizeof(u32), bad);
145         struct_v = ceph_decode_8(&p);
146         if (struct_v != 1)
147                 goto bad;
148         num = ceph_decode_32(&p);
149         dout("%d tickets\n", num);
150         while (num--) {
151                 int type;
152                 u8 struct_v;
153                 struct ceph_x_ticket_handler *th;
154                 void *dp, *dend;
155                 int dlen;
156                 char is_enc;
157                 struct timespec validity;
158                 struct ceph_crypto_key old_key;
159                 void *tp, *tpend;
160                 struct ceph_timespec new_validity;
161                 struct ceph_crypto_key new_session_key;
162                 struct ceph_buffer *new_ticket_blob;
163                 unsigned long new_expires, new_renew_after;
164                 u64 new_secret_id;
165
166                 ceph_decode_need(&p, end, sizeof(u32) + 1, bad);
167
168                 type = ceph_decode_32(&p);
169                 dout(" ticket type %d %s\n", type, ceph_entity_type_name(type));
170
171                 struct_v = ceph_decode_8(&p);
172                 if (struct_v != 1)
173                         goto bad;
174
175                 th = get_ticket_handler(ac, type);
176                 if (IS_ERR(th)) {
177                         ret = PTR_ERR(th);
178                         goto out;
179                 }
180
181                 /* blob for me */
182                 dlen = ceph_x_decrypt(secret, &p, end, dbuf,
183                                       TEMP_TICKET_BUF_LEN);
184                 if (dlen <= 0) {
185                         ret = dlen;
186                         goto out;
187                 }
188                 dout(" decrypted %d bytes\n", dlen);
189                 dend = dbuf + dlen;
190                 dp = dbuf;
191
192                 struct_v = ceph_decode_8(&dp);
193                 if (struct_v != 1)
194                         goto bad;
195
196                 memcpy(&old_key, &th->session_key, sizeof(old_key));
197                 ret = ceph_crypto_key_decode(&new_session_key, &dp, dend);
198                 if (ret)
199                         goto out;
200
201                 ceph_decode_copy(&dp, &new_validity, sizeof(new_validity));
202                 ceph_decode_timespec(&validity, &new_validity);
203                 new_expires = get_seconds() + validity.tv_sec;
204                 new_renew_after = new_expires - (validity.tv_sec / 4);
205                 dout(" expires=%lu renew_after=%lu\n", new_expires,
206                      new_renew_after);
207
208                 /* ticket blob for service */
209                 ceph_decode_8_safe(&p, end, is_enc, bad);
210                 tp = ticket_buf;
211                 if (is_enc) {
212                         /* encrypted */
213                         dout(" encrypted ticket\n");
214                         dlen = ceph_x_decrypt(&old_key, &p, end, ticket_buf,
215                                               TEMP_TICKET_BUF_LEN);
216                         if (dlen < 0) {
217                                 ret = dlen;
218                                 goto out;
219                         }
220                         dlen = ceph_decode_32(&tp);
221                 } else {
222                         /* unencrypted */
223                         ceph_decode_32_safe(&p, end, dlen, bad);
224                         ceph_decode_need(&p, end, dlen, bad);
225                         ceph_decode_copy(&p, ticket_buf, dlen);
226                 }
227                 tpend = tp + dlen;
228                 dout(" ticket blob is %d bytes\n", dlen);
229                 ceph_decode_need(&tp, tpend, 1 + sizeof(u64), bad);
230                 struct_v = ceph_decode_8(&tp);
231                 new_secret_id = ceph_decode_64(&tp);
232                 ret = ceph_decode_buffer(&new_ticket_blob, &tp, tpend);
233                 if (ret)
234                         goto out;
235
236                 /* all is well, update our ticket */
237                 ceph_crypto_key_destroy(&th->session_key);
238                 if (th->ticket_blob)
239                         ceph_buffer_put(th->ticket_blob);
240                 th->session_key = new_session_key;
241                 th->ticket_blob = new_ticket_blob;
242                 th->validity = new_validity;
243                 th->secret_id = new_secret_id;
244                 th->expires = new_expires;
245                 th->renew_after = new_renew_after;
246                 dout(" got ticket service %d (%s) secret_id %lld len %d\n",
247                      type, ceph_entity_type_name(type), th->secret_id,
248                      (int)th->ticket_blob->vec.iov_len);
249                 xi->have_keys |= th->service;
250         }
251
252         ret = 0;
253 out:
254         kmem_cache_free(ceph_x_ticketbuf_cachep, ticket_buf);
255 out_dbuf:
256         kmem_cache_free(ceph_x_ticketbuf_cachep, dbuf);
257         return ret;
258
259 bad:
260         ret = -EINVAL;
261         goto out;
262 }
263
264 static int ceph_x_build_authorizer(struct ceph_auth_client *ac,
265                                    struct ceph_x_ticket_handler *th,
266                                    struct ceph_x_authorizer *au)
267 {
268         int maxlen;
269         struct ceph_x_authorize_a *msg_a;
270         struct ceph_x_authorize_b msg_b;
271         void *p, *end;
272         int ret;
273         int ticket_blob_len =
274                 (th->ticket_blob ? th->ticket_blob->vec.iov_len : 0);
275
276         dout("build_authorizer for %s %p\n",
277              ceph_entity_type_name(th->service), au);
278
279         maxlen = sizeof(*msg_a) + sizeof(msg_b) +
280                 ceph_x_encrypt_buflen(ticket_blob_len);
281         dout("  need len %d\n", maxlen);
282         if (au->buf && au->buf->alloc_len < maxlen) {
283                 ceph_buffer_put(au->buf);
284                 au->buf = NULL;
285         }
286         if (!au->buf) {
287                 au->buf = ceph_buffer_new(maxlen, GFP_NOFS);
288                 if (!au->buf)
289                         return -ENOMEM;
290         }
291         au->service = th->service;
292
293         msg_a = au->buf->vec.iov_base;
294         msg_a->struct_v = 1;
295         msg_a->global_id = cpu_to_le64(ac->global_id);
296         msg_a->service_id = cpu_to_le32(th->service);
297         msg_a->ticket_blob.struct_v = 1;
298         msg_a->ticket_blob.secret_id = cpu_to_le64(th->secret_id);
299         msg_a->ticket_blob.blob_len = cpu_to_le32(ticket_blob_len);
300         if (ticket_blob_len) {
301                 memcpy(msg_a->ticket_blob.blob, th->ticket_blob->vec.iov_base,
302                        th->ticket_blob->vec.iov_len);
303         }
304         dout(" th %p secret_id %lld %lld\n", th, th->secret_id,
305              le64_to_cpu(msg_a->ticket_blob.secret_id));
306
307         p = msg_a + 1;
308         p += ticket_blob_len;
309         end = au->buf->vec.iov_base + au->buf->vec.iov_len;
310
311         get_random_bytes(&au->nonce, sizeof(au->nonce));
312         msg_b.struct_v = 1;
313         msg_b.nonce = cpu_to_le64(au->nonce);
314         ret = ceph_x_encrypt(&th->session_key, &msg_b, sizeof(msg_b),
315                              p, end - p);
316         if (ret < 0)
317                 goto out_buf;
318         p += ret;
319         au->buf->vec.iov_len = p - au->buf->vec.iov_base;
320         dout(" built authorizer nonce %llx len %d\n", au->nonce,
321              (int)au->buf->vec.iov_len);
322         BUG_ON(au->buf->vec.iov_len > maxlen);
323         return 0;
324
325 out_buf:
326         ceph_buffer_put(au->buf);
327         au->buf = NULL;
328         return ret;
329 }
330
331 static int ceph_x_encode_ticket(struct ceph_x_ticket_handler *th,
332                                 void **p, void *end)
333 {
334         ceph_decode_need(p, end, 1 + sizeof(u64), bad);
335         ceph_encode_8(p, 1);
336         ceph_encode_64(p, th->secret_id);
337         if (th->ticket_blob) {
338                 const char *buf = th->ticket_blob->vec.iov_base;
339                 u32 len = th->ticket_blob->vec.iov_len;
340
341                 ceph_encode_32_safe(p, end, len, bad);
342                 ceph_encode_copy_safe(p, end, buf, len, bad);
343         } else {
344                 ceph_encode_32_safe(p, end, 0, bad);
345         }
346
347         return 0;
348 bad:
349         return -ERANGE;
350 }
351
352 static void ceph_x_validate_tickets(struct ceph_auth_client *ac, int *pneed)
353 {
354         int want = ac->want_keys;
355         struct ceph_x_info *xi = ac->private;
356         int service;
357
358         *pneed = ac->want_keys & ~(xi->have_keys);
359
360         for (service = 1; service <= want; service <<= 1) {
361                 struct ceph_x_ticket_handler *th;
362
363                 if (!(ac->want_keys & service))
364                         continue;
365
366                 if (*pneed & service)
367                         continue;
368
369                 th = get_ticket_handler(ac, service);
370
371                 if (!th) {
372                         *pneed |= service;
373                         continue;
374                 }
375
376                 if (get_seconds() >= th->renew_after)
377                         *pneed |= service;
378                 if (get_seconds() >= th->expires)
379                         xi->have_keys &= ~service;
380         }
381 }
382
383
384 static int ceph_x_build_request(struct ceph_auth_client *ac,
385                                 void *buf, void *end)
386 {
387         struct ceph_x_info *xi = ac->private;
388         int need;
389         struct ceph_x_request_header *head = buf;
390         int ret;
391         struct ceph_x_ticket_handler *th =
392                 get_ticket_handler(ac, CEPH_ENTITY_TYPE_AUTH);
393
394         ceph_x_validate_tickets(ac, &need);
395
396         dout("build_request want %x have %x need %x\n",
397              ac->want_keys, xi->have_keys, need);
398
399         if (need & CEPH_ENTITY_TYPE_AUTH) {
400                 struct ceph_x_authenticate *auth = (void *)(head + 1);
401                 void *p = auth + 1;
402                 struct ceph_x_challenge_blob tmp;
403                 char tmp_enc[40];
404                 u64 *u;
405
406                 if (p > end)
407                         return -ERANGE;
408
409                 dout(" get_auth_session_key\n");
410                 head->op = cpu_to_le16(CEPHX_GET_AUTH_SESSION_KEY);
411
412                 /* encrypt and hash */
413                 get_random_bytes(&auth->client_challenge, sizeof(u64));
414                 tmp.client_challenge = auth->client_challenge;
415                 tmp.server_challenge = cpu_to_le64(xi->server_challenge);
416                 ret = ceph_x_encrypt(&xi->secret, &tmp, sizeof(tmp),
417                                      tmp_enc, sizeof(tmp_enc));
418                 if (ret < 0)
419                         return ret;
420
421                 auth->struct_v = 1;
422                 auth->key = 0;
423                 for (u = (u64 *)tmp_enc; u + 1 <= (u64 *)(tmp_enc + ret); u++)
424                         auth->key ^= *u;
425                 dout(" server_challenge %llx client_challenge %llx key %llx\n",
426                      xi->server_challenge, le64_to_cpu(auth->client_challenge),
427                      le64_to_cpu(auth->key));
428
429                 /* now encode the old ticket if exists */
430                 ret = ceph_x_encode_ticket(th, &p, end);
431                 if (ret < 0)
432                         return ret;
433
434                 return p - buf;
435         }
436
437         if (need) {
438                 void *p = head + 1;
439                 struct ceph_x_service_ticket_request *req;
440
441                 if (p > end)
442                         return -ERANGE;
443                 head->op = cpu_to_le16(CEPHX_GET_PRINCIPAL_SESSION_KEY);
444
445                 BUG_ON(!th);
446                 ret = ceph_x_build_authorizer(ac, th, &xi->auth_authorizer);
447                 if (ret)
448                         return ret;
449                 ceph_encode_copy(&p, xi->auth_authorizer.buf->vec.iov_base,
450                                  xi->auth_authorizer.buf->vec.iov_len);
451
452                 req = p;
453                 req->keys = cpu_to_le32(need);
454                 p += sizeof(*req);
455                 return p - buf;
456         }
457
458         return 0;
459 }
460
461 static int ceph_x_handle_reply(struct ceph_auth_client *ac, int result,
462                                void *buf, void *end)
463 {
464         struct ceph_x_info *xi = ac->private;
465         struct ceph_x_reply_header *head = buf;
466         struct ceph_x_ticket_handler *th;
467         int len = end - buf;
468         int op;
469         int ret;
470
471         if (result)
472                 return result;  /* XXX hmm? */
473
474         if (xi->starting) {
475                 /* it's a hello */
476                 struct ceph_x_server_challenge *sc = buf;
477
478                 if (len != sizeof(*sc))
479                         return -EINVAL;
480                 xi->server_challenge = le64_to_cpu(sc->server_challenge);
481                 dout("handle_reply got server challenge %llx\n",
482                      xi->server_challenge);
483                 xi->starting = false;
484                 xi->have_keys &= ~CEPH_ENTITY_TYPE_AUTH;
485                 return -EAGAIN;
486         }
487
488         op = le32_to_cpu(head->op);
489         result = le32_to_cpu(head->result);
490         dout("handle_reply op %d result %d\n", op, result);
491         switch (op) {
492         case CEPHX_GET_AUTH_SESSION_KEY:
493                 /* verify auth key */
494                 ret = ceph_x_proc_ticket_reply(ac, &xi->secret,
495                                                buf + sizeof(*head), end);
496                 break;
497
498         case CEPHX_GET_PRINCIPAL_SESSION_KEY:
499                 th = get_ticket_handler(ac, CEPH_ENTITY_TYPE_AUTH);
500                 BUG_ON(!th);
501                 ret = ceph_x_proc_ticket_reply(ac, &th->session_key,
502                                                buf + sizeof(*head), end);
503                 break;
504
505         default:
506                 return -EINVAL;
507         }
508         if (ret)
509                 return ret;
510         if (ac->want_keys == xi->have_keys)
511                 return 0;
512         return -EAGAIN;
513 }
514
515 static int ceph_x_create_authorizer(
516         struct ceph_auth_client *ac, int peer_type,
517         struct ceph_authorizer **a,
518         void **buf, size_t *len,
519         void **reply_buf, size_t *reply_len)
520 {
521         struct ceph_x_authorizer *au;
522         struct ceph_x_ticket_handler *th;
523         int ret;
524
525         th = get_ticket_handler(ac, peer_type);
526         if (IS_ERR(th))
527                 return PTR_ERR(th);
528
529         au = kzalloc(sizeof(*au), GFP_NOFS);
530         if (!au)
531                 return -ENOMEM;
532
533         ret = ceph_x_build_authorizer(ac, th, au);
534         if (ret) {
535                 kfree(au);
536                 return ret;
537         }
538
539         *a = (struct ceph_authorizer *)au;
540         *buf = au->buf->vec.iov_base;
541         *len = au->buf->vec.iov_len;
542         *reply_buf = au->reply_buf;
543         *reply_len = sizeof(au->reply_buf);
544         return 0;
545 }
546
547 static int ceph_x_verify_authorizer_reply(struct ceph_auth_client *ac,
548                                           struct ceph_authorizer *a, size_t len)
549 {
550         struct ceph_x_authorizer *au = (void *)a;
551         struct ceph_x_ticket_handler *th;
552         int ret = 0;
553         struct ceph_x_authorize_reply reply;
554         void *p = au->reply_buf;
555         void *end = p + sizeof(au->reply_buf);
556
557         th = get_ticket_handler(ac, au->service);
558         if (!th)
559                 return -EIO;  /* hrm! */
560         ret = ceph_x_decrypt(&th->session_key, &p, end, &reply, sizeof(reply));
561         if (ret < 0)
562                 return ret;
563         if (ret != sizeof(reply))
564                 return -EPERM;
565
566         if (au->nonce + 1 != le64_to_cpu(reply.nonce_plus_one))
567                 ret = -EPERM;
568         else
569                 ret = 0;
570         dout("verify_authorizer_reply nonce %llx got %llx ret %d\n",
571              au->nonce, le64_to_cpu(reply.nonce_plus_one), ret);
572         return ret;
573 }
574
575 static void ceph_x_destroy_authorizer(struct ceph_auth_client *ac,
576                                       struct ceph_authorizer *a)
577 {
578         struct ceph_x_authorizer *au = (void *)a;
579
580         ceph_buffer_put(au->buf);
581         kfree(au);
582 }
583
584
585 static void ceph_x_reset(struct ceph_auth_client *ac)
586 {
587         struct ceph_x_info *xi = ac->private;
588
589         dout("reset\n");
590         xi->starting = true;
591         xi->server_challenge = 0;
592 }
593
594 static void ceph_x_destroy(struct ceph_auth_client *ac)
595 {
596         struct ceph_x_info *xi = ac->private;
597         struct rb_node *p;
598
599         dout("ceph_x_destroy %p\n", ac);
600         ceph_crypto_key_destroy(&xi->secret);
601
602         while ((p = rb_first(&xi->ticket_handlers)) != NULL) {
603                 struct ceph_x_ticket_handler *th =
604                         rb_entry(p, struct ceph_x_ticket_handler, node);
605                 remove_ticket_handler(ac, th);
606         }
607
608         kmem_cache_destroy(ceph_x_ticketbuf_cachep);
609
610         kfree(ac->private);
611         ac->private = NULL;
612 }
613
614 static void ceph_x_invalidate_authorizer(struct ceph_auth_client *ac,
615                                    int peer_type)
616 {
617         struct ceph_x_ticket_handler *th;
618
619         th = get_ticket_handler(ac, peer_type);
620         if (th && !IS_ERR(th))
621                 remove_ticket_handler(ac, th);
622 }
623
624
625 static const struct ceph_auth_client_ops ceph_x_ops = {
626         .is_authenticated = ceph_x_is_authenticated,
627         .build_request = ceph_x_build_request,
628         .handle_reply = ceph_x_handle_reply,
629         .create_authorizer = ceph_x_create_authorizer,
630         .verify_authorizer_reply = ceph_x_verify_authorizer_reply,
631         .destroy_authorizer = ceph_x_destroy_authorizer,
632         .invalidate_authorizer = ceph_x_invalidate_authorizer,
633         .reset =  ceph_x_reset,
634         .destroy = ceph_x_destroy,
635 };
636
637
638 int ceph_x_init(struct ceph_auth_client *ac)
639 {
640         struct ceph_x_info *xi;
641         int ret;
642
643         dout("ceph_x_init %p\n", ac);
644         xi = kzalloc(sizeof(*xi), GFP_NOFS);
645         if (!xi)
646                 return -ENOMEM;
647
648         ret = -ENOMEM;
649         ceph_x_ticketbuf_cachep = kmem_cache_create("ceph_x_ticketbuf",
650                                       TEMP_TICKET_BUF_LEN, 8,
651                                       (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD),
652                                       NULL);
653         if (!ceph_x_ticketbuf_cachep)
654                 goto done_nomem;
655         ret = -EINVAL;
656         if (!ac->secret) {
657                 pr_err("no secret set (for auth_x protocol)\n");
658                 goto done_nomem;
659         }
660
661         ret = ceph_crypto_key_unarmor(&xi->secret, ac->secret);
662         if (ret)
663                 goto done_nomem;
664
665         xi->starting = true;
666         xi->ticket_handlers = RB_ROOT;
667
668         ac->protocol = CEPH_AUTH_CEPHX;
669         ac->private = xi;
670         ac->ops = &ceph_x_ops;
671         return 0;
672
673 done_nomem:
674         kfree(xi);
675         if (ceph_x_ticketbuf_cachep)
676                 kmem_cache_destroy(ceph_x_ticketbuf_cachep);
677         return ret;
678 }
679
680