1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
6 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 only,
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License version 2 for more details (a copy is included
16 * in the LICENSE file that accompanied this code).
18 * You should have received a copy of the GNU General Public License
19 * version 2 along with this program; If not, see
20 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
22 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23 * CA 95054 USA or visit www.sun.com if you need additional information or
29 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
30 * Use is subject to license terms.
32 * Copyright (c) 2011, Whamcloud, Inc.
35 * This file is part of Lustre, http://www.lustre.org/
36 * Lustre is a trademark of Sun Microsystems, Inc.
38 * lustre/utils/gss/lgss_keyring.c
40 * user-space upcall to create GSS context, using keyring interface to kernel
42 * Author: Eric Mei <ericm@clusterfs.com>
53 #include <gssapi/gssapi.h>
56 #include "lgss_utils.h"
57 #include "write_bytes.h"
61 * gss target string of lustre service we are negotiating for
63 static char *g_service = NULL;
66 * all data about negotiation
68 struct lgss_nego_data {
69 uint32_t lnd_established:1;
76 gss_OID lnd_mech; /* mech OID */
77 gss_name_t lnd_svc_name; /* service name */
78 u_int lnd_req_flags; /* request flags */
79 gss_cred_id_t lnd_cred; /* credential */
80 gss_ctx_id_t lnd_ctx; /* session context */
81 gss_buffer_desc lnd_rmt_ctx; /* remote handle of context */
82 uint32_t lnd_seq_win; /* sequence window */
89 * context creation response
91 struct lgss_init_res {
92 gss_buffer_desc gr_ctx; /* context handle */
93 u_int gr_major; /* major status */
94 u_int gr_minor; /* minor status */
95 u_int gr_win; /* sequence window */
96 gss_buffer_desc gr_token; /* token */
99 struct keyring_upcall_param {
110 unsigned int kup_is_root:1,
115 /****************************************
116 * child process: gss negotiation *
117 ****************************************/
119 #define INIT_CHANNEL "/proc/fs/lustre/sptlrpc/gss/init_channel"
121 int do_nego_rpc(struct lgss_nego_data *lnd,
122 gss_buffer_desc *gss_token,
123 struct lgss_init_res *gr)
125 struct lgssd_ioctl_param param;
131 logmsg(LL_TRACE, "start negotiation rpc\n");
133 pw = getpwuid(lnd->lnd_uid);
135 logmsg(LL_ERR, "no uid %u in local user database\n",
140 param.version = GSSD_INTERFACE_VERSION;
141 param.secid = lnd->lnd_secid;
142 param.uuid = lnd->lnd_uuid;
143 param.lustre_svc = lnd->lnd_lsvc;
144 param.uid = lnd->lnd_uid;
145 param.gid = pw->pw_gid;
146 param.send_token_size = gss_token->length;
147 param.send_token = (char *) gss_token->value;
148 param.reply_buf_size = sizeof(outbuf);
149 param.reply_buf = outbuf;
151 logmsg(LL_TRACE, "to open " INIT_CHANNEL "\n");
153 fd = open(INIT_CHANNEL, O_WRONLY);
155 logmsg(LL_ERR, "can't open " INIT_CHANNEL "\n");
159 logmsg(LL_TRACE, "to down-write\n");
161 ret = write(fd, ¶m, sizeof(param));
162 if (ret != sizeof(param)) {
163 logmsg(LL_ERR, "lustre ioctl err: %d\n", strerror(errno));
169 logmsg(LL_TRACE, "do_nego_rpc: to parse reply\n");
171 logmsg(LL_ERR, "status: %d (%s)\n",
172 param.status, strerror((int)param.status));
174 /* kernel return -ETIMEDOUT means the rpc timedout, we should
175 * notify the caller to reinitiate the gss negotiation, by
176 * returning -ERESTART
178 if (param.status == -ETIMEDOUT)
184 p = (unsigned int *)outbuf;
190 gr->gr_ctx.length = *p++;
191 gr->gr_ctx.value = malloc(gr->gr_ctx.length);
192 memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
193 p += (((gr->gr_ctx.length + 3) & ~3) / 4);
195 gr->gr_token.length = *p++;
196 gr->gr_token.value = malloc(gr->gr_token.length);
197 memcpy(gr->gr_token.value, p, gr->gr_token.length);
198 p += (((gr->gr_token.length + 3) & ~3) / 4);
200 logmsg(LL_DEBUG, "do_nego_rpc: receive handle len %d, token len %d\n",
201 gr->gr_ctx.length, gr->gr_token.length);
206 * if return error, the lnd_rpc_err or lnd_gss_err is set.
208 static int lgssc_negotiation(struct lgss_nego_data *lnd)
210 struct lgss_init_res gr;
211 gss_buffer_desc *recv_tokenp, send_token;
212 OM_uint32 maj_stat, min_stat, ret_flags;
214 logmsg(LL_TRACE, "start gss negotiation\n");
216 /* GSS context establishment loop. */
217 memset(&gr, 0, sizeof(gr));
218 recv_tokenp = GSS_C_NO_BUFFER;
221 maj_stat = gss_init_sec_context(&min_stat,
230 NULL, /* used mech */
233 NULL); /* time rec */
235 if (recv_tokenp != GSS_C_NO_BUFFER) {
236 gss_release_buffer(&min_stat, &gr.gr_token);
237 recv_tokenp = GSS_C_NO_BUFFER;
240 if (maj_stat != GSS_S_COMPLETE &&
241 maj_stat != GSS_S_CONTINUE_NEEDED) {
242 lnd->lnd_gss_err = maj_stat;
244 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
245 "failed init context");
249 if (send_token.length != 0) {
250 memset(&gr, 0, sizeof(gr));
252 lnd->lnd_rpc_err = do_nego_rpc(lnd, &send_token, &gr);
253 gss_release_buffer(&min_stat, &send_token);
255 if (lnd->lnd_rpc_err) {
256 logmsg(LL_ERR, "negotiation rpc error: %d\n",
261 if (gr.gr_major != GSS_S_COMPLETE &&
262 gr.gr_major != GSS_S_CONTINUE_NEEDED) {
263 lnd->lnd_gss_err = gr.gr_major;
265 logmsg(LL_ERR, "negotiation gss error %x\n",
270 if (gr.gr_ctx.length != 0) {
271 if (lnd->lnd_rmt_ctx.value)
272 gss_release_buffer(&min_stat,
274 lnd->lnd_rmt_ctx = gr.gr_ctx;
277 if (gr.gr_token.length != 0) {
278 if (maj_stat != GSS_S_CONTINUE_NEEDED)
280 recv_tokenp = &gr.gr_token;
284 /* GSS_S_COMPLETE => check gss header verifier,
285 * usually checked in gss_validate
287 if (maj_stat == GSS_S_COMPLETE) {
288 lnd->lnd_established = 1;
289 lnd->lnd_seq_win = gr.gr_win;
294 /* End context negotiation loop. */
295 if (!lnd->lnd_established) {
296 if (gr.gr_token.length != 0)
297 gss_release_buffer(&min_stat, &gr.gr_token);
299 if (lnd->lnd_gss_err == GSS_S_COMPLETE)
300 lnd->lnd_rpc_err = -EACCES;
302 logmsg(LL_ERR, "context negotiation failed\n");
306 logmsg(LL_DEBUG, "successfully negotiated a context\n");
311 * if return error, the lnd_rpc_err or lnd_gss_err is set.
313 static int lgssc_init_nego_data(struct lgss_nego_data *lnd,
314 struct keyring_upcall_param *kup,
317 gss_buffer_desc sname;
318 OM_uint32 maj_stat, min_stat;
320 memset(lnd, 0, sizeof(*lnd));
322 lnd->lnd_secid = kup->kup_secid;
323 lnd->lnd_uid = kup->kup_uid;
324 lnd->lnd_lsvc = kup->kup_svc;
325 lnd->lnd_uuid = kup->kup_tgt;
327 lnd->lnd_established = 0;
328 lnd->lnd_svc_name = GSS_C_NO_NAME;
329 lnd->lnd_cred = GSS_C_NO_CREDENTIAL;
330 lnd->lnd_ctx = GSS_C_NO_CONTEXT;
331 lnd->lnd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
332 lnd->lnd_seq_win = 0;
336 lnd->lnd_mech = (gss_OID) &krb5oid;
337 lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG;
340 logmsg(LL_ERR, "invalid mech: %d\n", mech);
341 lnd->lnd_rpc_err = -EACCES;
345 sname.value = g_service;
346 sname.length = strlen(g_service);
348 maj_stat = gss_import_name(&min_stat, &sname,
349 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
351 if (maj_stat != GSS_S_COMPLETE) {
352 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
353 "can't import svc name");
354 lnd->lnd_gss_err = maj_stat;
361 void lgssc_fini_nego_data(struct lgss_nego_data *lnd)
363 OM_uint32 maj_stat, min_stat;
365 if (lnd->lnd_svc_name != GSS_C_NO_NAME) {
366 maj_stat = gss_release_name(&min_stat, &lnd->lnd_svc_name);
367 if (maj_stat != GSS_S_COMPLETE)
368 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
369 "can't release service name");
372 if (lnd->lnd_cred != GSS_C_NO_CREDENTIAL) {
373 maj_stat = gss_release_cred(&min_stat, &lnd->lnd_cred);
374 if (maj_stat != GSS_S_COMPLETE)
375 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
376 "can't release credential");
381 int error_kernel_key(key_serial_t keyid, int rpc_error, int gss_error)
387 logmsg(LL_TRACE, "revoking kernel key %08x\n", keyid);
390 end = buf + sizeof(buf);
392 WRITE_BYTES(&p, end, seqwin);
393 WRITE_BYTES(&p, end, rpc_error);
394 WRITE_BYTES(&p, end, gss_error);
397 if (keyctl_update(keyid, buf, p - buf)) {
398 if (errno != EAGAIN) {
399 logmsg(LL_ERR, "revoke key %08x: %s\n",
400 keyid, strerror(errno));
404 logmsg(LL_WARN, "key %08x: revoking too soon, try again\n",
410 logmsg(LL_INFO, "key %08x: revoked\n", keyid);
415 int update_kernel_key(key_serial_t keyid,
416 struct lgss_nego_data *lnd,
417 gss_buffer_desc *ctx_token)
419 char *buf = NULL, *p = NULL, *end = NULL;
420 unsigned int buf_size = 0;
423 logmsg(LL_TRACE, "updating kernel key %08x\n", keyid);
425 buf_size = sizeof(lnd->lnd_seq_win) +
426 sizeof(lnd->lnd_rmt_ctx.length) + lnd->lnd_rmt_ctx.length +
427 sizeof(ctx_token->length) + ctx_token->length;
428 buf = malloc(buf_size);
430 logmsg(LL_ERR, "key %08x: can't alloc update buf: size %d\n",
436 end = buf + buf_size;
439 if (WRITE_BYTES(&p, end, lnd->lnd_seq_win))
441 if (write_buffer(&p, end, &lnd->lnd_rmt_ctx))
443 if (write_buffer(&p, end, ctx_token))
447 if (keyctl_update(keyid, buf, p - buf)) {
448 if (errno != EAGAIN) {
449 logmsg(LL_ERR, "update key %08x: %s\n",
450 keyid, strerror(errno));
454 logmsg(LL_DEBUG, "key %08x: updating too soon, try again\n",
461 logmsg(LL_DEBUG, "key %08x: updated\n", keyid);
468 * note we inherited assumed authority from parent process
470 static int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred,
471 struct keyring_upcall_param *kup)
473 struct lgss_nego_data lnd;
474 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
478 logmsg(LL_TRACE, "child start on behalf of key %08x: "
479 "cred %p, uid %u, svc %u, nid %llx, uids: %u:%u/%u:%u\n",
480 keyid, cred, cred->lc_uid, cred->lc_tgt_svc, cred->lc_tgt_nid,
481 kup->kup_uid, kup->kup_gid, kup->kup_fsuid, kup->kup_fsgid);
483 if (lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid)) {
484 logmsg(LL_ERR, "key %08x: failed to construct service "
486 error_kernel_key(keyid, -EACCES, 0);
490 if (lgss_using_cred(cred)) {
491 logmsg(LL_ERR, "key %08x: can't using cred\n", keyid);
492 error_kernel_key(keyid, -EACCES, 0);
496 if (lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n)) {
497 logmsg(LL_ERR, "key %08x: failed to initialize "
498 "negotiation data\n", keyid);
499 error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
503 rc = lgssc_negotiation(&lnd);
505 logmsg(LL_ERR, "key %08x: failed to negotiation\n", keyid);
506 error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
510 rc = serialize_context_for_kernel(lnd.lnd_ctx, &token, lnd.lnd_mech);
512 logmsg(LL_ERR, "key %08x: failed to export context\n", keyid);
513 error_kernel_key(keyid, rc, lnd.lnd_gss_err);
517 rc = update_kernel_key(keyid, &lnd, &token);
522 logmsg(LL_INFO, "key %08x for user %u is updated OK!\n",
523 keyid, kup->kup_uid);
525 if (token.length != 0)
526 gss_release_buffer(&min_stat, &token);
528 lgssc_fini_nego_data(&lnd);
531 lgss_release_cred(cred);
536 * call out info format: s[:s]...
538 * [1]: mech_name (string)
541 * [4]: flags (string) FMT: r-root; m-mdt; o-ost
542 * [5]: lustre_svc (uint)
543 * [6]: target_nid (uint64)
544 * [7]: target_uuid (string)
546 static int parse_callout_info(const char *coinfo,
547 struct keyring_upcall_param *uparam)
556 length = strlen(coinfo) + 1;
558 logmsg(LL_ERR, "coinfo too long\n");
561 memcpy(buf, coinfo, length);
563 for (i = 0; i < nargs - 1; i++) {
564 pos = strchr(string, ':');
566 logmsg(LL_ERR, "short of components\n");
576 logmsg(LL_TRACE, "components: %s,%s,%s,%s,%s,%s,%s,%s\n",
577 data[0], data[1], data[2], data[3], data[4], data[5],
580 uparam->kup_secid = strtol(data[0], NULL, 0);
581 strncpy(uparam->kup_mech, data[1], sizeof(uparam->kup_mech));
582 uparam->kup_uid = strtol(data[2], NULL, 0);
583 uparam->kup_gid = strtol(data[3], NULL, 0);
584 if (strchr(data[4], 'r'))
585 uparam->kup_is_root = 1;
586 if (strchr(data[4], 'm'))
587 uparam->kup_is_mdt = 1;
588 if (strchr(data[4], 'o'))
589 uparam->kup_is_ost = 1;
590 uparam->kup_svc = strtol(data[5], NULL, 0);
591 uparam->kup_nid = strtoll(data[6], NULL, 0);
592 strncpy(uparam->kup_tgt, data[7], sizeof(uparam->kup_tgt));
594 logmsg(LL_DEBUG, "parse call out info: secid %d, mech %s, ugid %u:%u "
595 "is_root %d, is_mdt %d, is_ost %d, svc %d, nid 0x%llx, tgt %s\n",
596 uparam->kup_secid, uparam->kup_mech,
597 uparam->kup_uid, uparam->kup_gid,
598 uparam->kup_is_root, uparam->kup_is_mdt, uparam->kup_is_ost,
599 uparam->kup_svc, uparam->kup_nid, uparam->kup_tgt);
603 #define LOG_LEVEL_PATH "/proc/fs/lustre/sptlrpc/gss/lgss_keyring/debug_level"
605 static void set_log_level()
610 file = fopen(LOG_LEVEL_PATH, "r");
614 if (fscanf(file, "%u", &level) != 1)
620 lgss_set_loglevel(level);
625 /****************************************
627 ****************************************/
629 int main(int argc, char *argv[])
631 struct keyring_upcall_param uparam;
634 key_serial_t inst_keyring;
636 struct lgss_mech_type *mech;
637 struct lgss_cred *cred;
641 logmsg(LL_TRACE, "start parsing parameters\n");
643 * parse & sanity check upcall parameters
644 * expected to be called with:
648 * [4]: key description
652 * [8]: thread keyring
653 * [9]: process keyring
654 * [10]: session keyring
656 if (argc != 10 + 1) {
657 logmsg(LL_ERR, "invalid parameter number %d\n", argc);
661 logmsg(LL_INFO, "key %s, desc %s, ugid %s:%s, sring %s, coinfo %s\n",
662 argv[2], argv[4], argv[6], argv[7], argv[10], argv[5]);
664 memset(&uparam, 0, sizeof(uparam));
666 if (strcmp(argv[1], "create") != 0) {
667 logmsg(LL_ERR, "invalid OP %s\n", argv[1]);
671 if (sscanf(argv[2], "%d", &keyid) != 1) {
672 logmsg(LL_ERR, "can't extract KeyID: %s\n", argv[2]);
676 if (sscanf(argv[6], "%d", &uparam.kup_fsuid) != 1) {
677 logmsg(LL_ERR, "can't extract UID: %s\n", argv[6]);
681 if (sscanf(argv[7], "%d", &uparam.kup_fsgid) != 1) {
682 logmsg(LL_ERR, "can't extract GID: %s\n", argv[7]);
686 if (sscanf(argv[10], "%d", &sring) != 1) {
687 logmsg(LL_ERR, "can't extract session keyring: %s\n", argv[10]);
691 if (parse_callout_info(argv[5], &uparam)) {
692 logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]);
696 logmsg(LL_TRACE, "parsing parameters OK\n");
701 mech = lgss_name2mech(uparam.kup_mech);
703 logmsg(LL_ERR, "key %08x: unsupported mech: %s\n",
704 keyid, uparam.kup_mech);
708 if (lgss_mech_initialize(mech)) {
709 logmsg(LL_ERR, "key %08x: can't initialize mech %s\n",
710 keyid, mech->lmt_name);
714 cred = lgss_create_cred(mech);
716 logmsg(LL_ERR, "key %08x: can't create a new %s cred\n",
717 keyid, mech->lmt_name);
721 cred->lc_uid = uparam.kup_uid;
722 cred->lc_root_flags |= uparam.kup_is_root ? LGSS_ROOT_CRED_ROOT : 0;
723 cred->lc_root_flags |= uparam.kup_is_mdt ? LGSS_ROOT_CRED_MDT : 0;
724 cred->lc_root_flags |= uparam.kup_is_ost ? LGSS_ROOT_CRED_OST : 0;
725 cred->lc_tgt_nid = uparam.kup_nid;
726 cred->lc_tgt_svc = uparam.kup_svc;
728 if (lgss_prepare_cred(cred)) {
729 logmsg(LL_ERR, "key %08x: failed to prepare credentials "
730 "for user %d\n", keyid, uparam.kup_uid);
734 /* pre initialize the key. note the keyring linked to is actually of the
735 * original requesting process, not _this_ upcall process. if it's for
736 * root user, don't link to any keyrings because we want fully control
737 * on it, and share it among all root sessions; otherswise link to
740 if (cred->lc_root_flags != 0)
743 inst_keyring = KEY_SPEC_SESSION_KEYRING;
745 if (keyctl_instantiate(keyid, NULL, 0, inst_keyring)) {
746 logmsg(LL_ERR, "instantiate key %08x: %s\n",
747 keyid, strerror(errno));
751 logmsg(LL_TRACE, "instantiated kernel key %08x\n", keyid);
754 * fork a child to do the real gss negotiation
758 logmsg(LL_ERR, "key %08x: can't create child: %s\n",
759 keyid, strerror(errno));
761 } else if (child == 0) {
762 return lgssc_kr_negotiate(keyid, cred, &uparam);
765 logmsg(LL_TRACE, "forked child %d\n", child);