1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
5 * user-space upcall to create GSS context, using keyring interface to kernel
7 * Copyright (c) 2007 Cluster File Systems, Inc.
8 * Author: Eric Mei <ericm@clusterfs.com>
10 * This file is part of the Lustre file system, http://www.lustre.org
11 * Lustre is a trademark of Cluster File Systems, Inc.
13 * You may have signed or agreed to another license before downloading
14 * this software. If so, you are bound by the terms and conditions
15 * of that agreement, and the following does not apply to you. See the
16 * LICENSE file included with this distribution for more information.
18 * If you did not agree to a different license, then this copy of Lustre
19 * is open source software; you can redistribute it and/or modify it
20 * under the terms of version 2 of the GNU General Public License as
21 * published by the Free Software Foundation.
23 * In either case, Lustre is distributed in the hope that it will be
24 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
25 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * license text for more details.
37 #include <gssapi/gssapi.h>
39 #include <libcfs/libcfs.h>
42 #include "lgss_utils.h"
43 #include "write_bytes.h"
47 * gss target string of lustre service we are negotiating for
49 static char *g_service = NULL;
52 * all data about negotiation
54 struct lgss_nego_data {
55 uint32_t lnd_established:1;
62 gss_OID lnd_mech; /* mech OID */
63 gss_name_t lnd_svc_name; /* service name */
64 u_int lnd_req_flags; /* request flags */
65 gss_cred_id_t lnd_cred; /* credential */
66 gss_ctx_id_t lnd_ctx; /* session context */
67 gss_buffer_desc lnd_rmt_ctx; /* remote handle of context */
68 uint32_t lnd_seq_win; /* sequence window */
75 * context creation response
77 struct lgss_init_res {
78 gss_buffer_desc gr_ctx; /* context handle */
79 u_int gr_major; /* major status */
80 u_int gr_minor; /* minor status */
81 u_int gr_win; /* sequence window */
82 gss_buffer_desc gr_token; /* token */
85 struct keyring_upcall_param {
100 /****************************************
101 * child process: gss negotiation *
102 ****************************************/
104 #define INIT_CHANNEL "/proc/fs/lustre/sptlrpc/gss/init_channel"
106 int do_nego_rpc(struct lgss_nego_data *lnd,
107 gss_buffer_desc *gss_token,
108 struct lgss_init_res *gr)
110 struct lgssd_ioctl_param param;
116 logmsg(LL_TRACE, "start negotiation rpc\n");
118 pw = getpwuid(lnd->lnd_uid);
120 logmsg(LL_ERR, "no uid %u in local user database\n",
125 param.version = GSSD_INTERFACE_VERSION;
126 param.secid = lnd->lnd_secid;
127 param.uuid = lnd->lnd_uuid;
128 param.lustre_svc = lnd->lnd_lsvc;
129 param.uid = lnd->lnd_uid;
130 param.gid = pw->pw_gid;
131 param.send_token_size = gss_token->length;
132 param.send_token = (char *) gss_token->value;
133 param.reply_buf_size = sizeof(outbuf);
134 param.reply_buf = outbuf;
136 logmsg(LL_TRACE, "to open " INIT_CHANNEL "\n");
138 fd = open(INIT_CHANNEL, O_WRONLY);
140 logmsg(LL_ERR, "can't open " INIT_CHANNEL "\n");
144 logmsg(LL_TRACE, "to down-write\n");
146 ret = write(fd, ¶m, sizeof(param));
147 if (ret != sizeof(param)) {
148 logmsg(LL_ERR, "lustre ioctl err: %d\n", strerror(errno));
154 logmsg(LL_TRACE, "do_nego_rpc: to parse reply\n");
156 logmsg(LL_ERR, "status: %d (%s)\n",
157 param.status, strerror((int)param.status));
159 /* kernel return -ETIMEDOUT means the rpc timedout, we should
160 * notify the caller to reinitiate the gss negotiation, by
161 * returning -ERESTART
163 if (param.status == -ETIMEDOUT)
169 p = (unsigned int *)outbuf;
175 gr->gr_ctx.length = *p++;
176 gr->gr_ctx.value = malloc(gr->gr_ctx.length);
177 memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
178 p += (((gr->gr_ctx.length + 3) & ~3) / 4);
180 gr->gr_token.length = *p++;
181 gr->gr_token.value = malloc(gr->gr_token.length);
182 memcpy(gr->gr_token.value, p, gr->gr_token.length);
183 p += (((gr->gr_token.length + 3) & ~3) / 4);
185 logmsg(LL_DEBUG, "do_nego_rpc: receive handle len %d, token len %d\n",
186 gr->gr_ctx.length, gr->gr_token.length);
191 * if return error, the lnd_rpc_err or lnd_gss_err is set.
193 static int lgssc_negotiation(struct lgss_nego_data *lnd)
195 struct lgss_init_res gr;
196 gss_buffer_desc *recv_tokenp, send_token;
197 OM_uint32 maj_stat, min_stat, ret_flags;
199 logmsg(LL_TRACE, "start gss negotiation\n");
201 /* GSS context establishment loop. */
202 memset(&gr, 0, sizeof(gr));
203 recv_tokenp = GSS_C_NO_BUFFER;
206 maj_stat = gss_init_sec_context(&min_stat,
215 NULL, /* used mech */
218 NULL); /* time rec */
220 if (recv_tokenp != GSS_C_NO_BUFFER) {
221 gss_release_buffer(&min_stat, &gr.gr_token);
222 recv_tokenp = GSS_C_NO_BUFFER;
225 if (maj_stat != GSS_S_COMPLETE &&
226 maj_stat != GSS_S_CONTINUE_NEEDED) {
227 lnd->lnd_gss_err = maj_stat;
229 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
230 "failed init context");
234 if (send_token.length != 0) {
235 memset(&gr, 0, sizeof(gr));
237 lnd->lnd_rpc_err = do_nego_rpc(lnd, &send_token, &gr);
238 gss_release_buffer(&min_stat, &send_token);
240 if (lnd->lnd_rpc_err) {
241 logmsg(LL_ERR, "negotiation rpc error: %d\n",
246 if (gr.gr_major != GSS_S_COMPLETE &&
247 gr.gr_major != GSS_S_CONTINUE_NEEDED) {
248 lnd->lnd_gss_err = gr.gr_major;
250 logmsg(LL_ERR, "negotiation gss error %x\n",
255 if (gr.gr_ctx.length != 0) {
256 if (lnd->lnd_rmt_ctx.value)
257 gss_release_buffer(&min_stat,
259 lnd->lnd_rmt_ctx = gr.gr_ctx;
262 if (gr.gr_token.length != 0) {
263 if (maj_stat != GSS_S_CONTINUE_NEEDED)
265 recv_tokenp = &gr.gr_token;
269 /* GSS_S_COMPLETE => check gss header verifier,
270 * usually checked in gss_validate
272 if (maj_stat == GSS_S_COMPLETE) {
273 lnd->lnd_established = 1;
274 lnd->lnd_seq_win = gr.gr_win;
279 /* End context negotiation loop. */
280 if (!lnd->lnd_established) {
281 if (gr.gr_token.length != 0)
282 gss_release_buffer(&min_stat, &gr.gr_token);
284 if (lnd->lnd_gss_err == GSS_S_COMPLETE)
285 lnd->lnd_rpc_err = -EACCES;
287 logmsg(LL_ERR, "context negotiation failed\n");
291 logmsg(LL_DEBUG, "successfully negotiated a context\n");
296 * if return error, the lnd_rpc_err or lnd_gss_err is set.
298 static int lgssc_init_nego_data(struct lgss_nego_data *lnd,
299 struct keyring_upcall_param *kup,
302 gss_buffer_desc sname;
303 OM_uint32 maj_stat, min_stat;
305 memset(lnd, 0, sizeof(*lnd));
307 lnd->lnd_secid = kup->kup_secid;
308 lnd->lnd_uid = kup->kup_uid;
309 lnd->lnd_lsvc = kup->kup_svc;
310 lnd->lnd_uuid = kup->kup_tgt;
312 lnd->lnd_established = 0;
313 lnd->lnd_svc_name = GSS_C_NO_NAME;
314 lnd->lnd_cred = GSS_C_NO_CREDENTIAL;
315 lnd->lnd_ctx = GSS_C_NO_CONTEXT;
316 lnd->lnd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
317 lnd->lnd_seq_win = 0;
321 lnd->lnd_mech = (gss_OID) &krb5oid;
322 lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG;
325 logmsg(LL_ERR, "invalid mech: %d\n", mech);
326 lnd->lnd_rpc_err = -EACCES;
330 sname.value = g_service;
331 sname.length = strlen(g_service);
333 maj_stat = gss_import_name(&min_stat, &sname,
334 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
336 if (maj_stat != GSS_S_COMPLETE) {
337 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
338 "can't import svc name");
339 lnd->lnd_gss_err = maj_stat;
346 void lgssc_fini_nego_data(struct lgss_nego_data *lnd)
348 OM_uint32 maj_stat, min_stat;
350 if (lnd->lnd_svc_name != GSS_C_NO_NAME) {
351 maj_stat = gss_release_name(&min_stat, &lnd->lnd_svc_name);
352 if (maj_stat != GSS_S_COMPLETE)
353 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
354 "can't release service name");
357 if (lnd->lnd_cred != GSS_C_NO_CREDENTIAL) {
358 maj_stat = gss_release_cred(&min_stat, &lnd->lnd_cred);
359 if (maj_stat != GSS_S_COMPLETE)
360 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
361 "can't release credential");
366 int error_kernel_key(key_serial_t keyid, int rpc_error, int gss_error)
372 logmsg(LL_TRACE, "revoking kernel key %08x\n", keyid);
375 end = buf + sizeof(buf);
377 WRITE_BYTES(&p, end, seqwin);
378 WRITE_BYTES(&p, end, rpc_error);
379 WRITE_BYTES(&p, end, gss_error);
382 if (keyctl_update(keyid, buf, p - buf)) {
383 if (errno != EAGAIN) {
384 logmsg(LL_ERR, "revoke key %08x: %s\n",
385 keyid, strerror(errno));
389 logmsg(LL_WARN, "key %08x: revoking too soon, try again\n",
395 logmsg(LL_INFO, "key %08x: revoked\n", keyid);
400 int update_kernel_key(key_serial_t keyid,
401 struct lgss_nego_data *lnd,
402 gss_buffer_desc *ctx_token)
404 char *buf = NULL, *p = NULL, *end = NULL;
405 unsigned int buf_size = 0;
408 logmsg(LL_TRACE, "updating kernel key %08x\n", keyid);
410 buf_size = sizeof(lnd->lnd_seq_win) +
411 sizeof(lnd->lnd_rmt_ctx.length) + lnd->lnd_rmt_ctx.length +
412 sizeof(ctx_token->length) + ctx_token->length;
413 buf = malloc(buf_size);
415 logmsg(LL_ERR, "key %08x: can't alloc update buf: size %d\n",
421 end = buf + buf_size;
424 if (WRITE_BYTES(&p, end, lnd->lnd_seq_win))
426 if (write_buffer(&p, end, &lnd->lnd_rmt_ctx))
428 if (write_buffer(&p, end, ctx_token))
432 if (keyctl_update(keyid, buf, p - buf)) {
433 if (errno != EAGAIN) {
434 logmsg(LL_ERR, "update key %08x: %s\n",
435 keyid, strerror(errno));
439 logmsg(LL_DEBUG, "key %08x: updating too soon, try again\n",
446 logmsg(LL_DEBUG, "key %08x: updated\n", keyid);
453 * note we inherited assumed authority from parent process
455 static int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred,
456 struct keyring_upcall_param *kup)
458 struct lgss_nego_data lnd;
459 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
463 logmsg(LL_TRACE, "child start on behalf of key %08x: "
464 "cred %p, uid %u, svc %u, nid %Lx, uids: %u:%u/%u:%u\n",
465 keyid, cred, cred->lc_uid, cred->lc_tgt_svc, cred->lc_tgt_nid,
466 kup->kup_uid, kup->kup_gid, kup->kup_fsuid, kup->kup_fsgid);
468 if (lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid)) {
469 logmsg(LL_ERR, "key %08x: failed to construct service "
471 error_kernel_key(keyid, -EACCES, 0);
475 if (lgss_using_cred(cred)) {
476 logmsg(LL_ERR, "key %08x: can't using cred\n", keyid);
477 error_kernel_key(keyid, -EACCES, 0);
481 if (lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n)) {
482 logmsg(LL_ERR, "key %08x: failed to initialize "
483 "negotiation data\n", keyid);
484 error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
488 rc = lgssc_negotiation(&lnd);
490 logmsg(LL_ERR, "key %08x: failed to negotiation\n", keyid);
491 error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
495 rc = serialize_context_for_kernel(lnd.lnd_ctx, &token, lnd.lnd_mech);
497 logmsg(LL_ERR, "key %08x: failed to export context\n", keyid);
498 error_kernel_key(keyid, rc, lnd.lnd_gss_err);
502 rc = update_kernel_key(keyid, &lnd, &token);
507 logmsg(LL_INFO, "key %08x for user %u is updated OK!\n",
508 keyid, kup->kup_uid);
510 if (token.length != 0)
511 gss_release_buffer(&min_stat, &token);
513 lgssc_fini_nego_data(&lnd);
516 lgss_release_cred(cred);
521 * call out info format: s[:s]...
523 * [1]: mech_name (string)
526 * [4]: flags (chars) FMT: r-root; m-mds
527 * [5]: lustre_svc (uint)
528 * [6]: target_nid (uint64)
529 * [7]: target_uuid (string)
531 static int parse_callout_info(const char *coinfo,
532 struct keyring_upcall_param *uparam)
541 length = strlen(coinfo) + 1;
543 logmsg(LL_ERR, "coinfo too long\n");
546 memcpy(buf, coinfo, length);
548 for (i = 0; i < nargs - 1; i++) {
549 pos = strchr(string, ':');
551 logmsg(LL_ERR, "short of components\n");
561 logmsg(LL_TRACE, "components: %s,%s,%s,%s,%s,%s,%s,%s\n",
562 data[0], data[1], data[2], data[3], data[4], data[5],
565 uparam->kup_secid = strtol(data[0], NULL, 0);
566 strncpy(uparam->kup_mech, data[1], sizeof(uparam->kup_mech));
567 uparam->kup_uid = strtol(data[2], NULL, 0);
568 uparam->kup_gid = strtol(data[3], NULL, 0);
569 if (strchr(data[4], 'r'))
570 uparam->kup_is_root = 1;
571 if (strchr(data[4], 'm'))
572 uparam->kup_is_mds = 1;
573 uparam->kup_svc = strtol(data[5], NULL, 0);
574 uparam->kup_nid = strtoll(data[6], NULL, 0);
575 strncpy(uparam->kup_tgt, data[7], sizeof(uparam->kup_tgt));
577 logmsg(LL_DEBUG, "parse call out info: secid %d, mech %s, ugid %u:%u "
578 "is_root %d, is_mds %d, svc %d, nid 0x%Lx, tgt %s\n",
579 uparam->kup_secid, uparam->kup_mech,
580 uparam->kup_uid, uparam->kup_gid,
581 uparam->kup_is_root, uparam->kup_is_mds, uparam->kup_svc,
582 uparam->kup_nid, uparam->kup_tgt);
586 /****************************************
588 ****************************************/
590 int main(int argc, char *argv[])
592 struct keyring_upcall_param uparam;
595 key_serial_t inst_keyring;
597 struct lgss_mech_type *mech;
598 struct lgss_cred *cred;
601 * parse & sanity check upcall parameters
602 * expected to be called with:
606 * [4]: key description
610 * [8]: thread keyring
611 * [9]: process keyring
612 * [10]: session keyring
614 if (argc != 10 + 1) {
615 logmsg(LL_ERR, "invalid parameter number %d\n", argc);
619 logmsg(LL_INFO, "key %s, desc %s, ugid %s:%s, sring %s, coinfo %s\n",
620 argv[2], argv[4], argv[6], argv[7], argv[10], argv[5]);
622 memset(&uparam, 0, sizeof(uparam));
624 if (strcmp(argv[1], "create") != 0) {
625 logmsg(LL_ERR, "invalid OP %s\n", argv[1]);
629 if (sscanf(argv[2], "%d", &keyid) != 1) {
630 logmsg(LL_ERR, "can't extract KeyID: %s\n", argv[2]);
634 if (sscanf(argv[6], "%d", &uparam.kup_fsuid) != 1) {
635 logmsg(LL_ERR, "can't extract UID: %s\n", argv[6]);
639 if (sscanf(argv[7], "%d", &uparam.kup_fsgid) != 1) {
640 logmsg(LL_ERR, "can't extract GID: %s\n", argv[7]);
644 if (sscanf(argv[10], "%d", &sring) != 1) {
645 logmsg(LL_ERR, "can't extract session keyring: %s\n", argv[10]);
649 if (parse_callout_info(argv[5], &uparam)) {
650 logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]);
654 logmsg(LL_TRACE, "parsing parameters OK\n");
659 mech = lgss_name2mech(uparam.kup_mech);
661 logmsg(LL_ERR, "key %08x: unsupported mech: %s\n",
662 keyid, uparam.kup_mech);
666 if (lgss_mech_initialize(mech)) {
667 logmsg(LL_ERR, "key %08x: can't initialize mech %s\n",
668 keyid, mech->lmt_name);
672 cred = lgss_create_cred(mech);
674 logmsg(LL_ERR, "key %08x: can't create a new %s cred\n",
675 keyid, mech->lmt_name);
679 cred->lc_uid = uparam.kup_uid;
680 cred->lc_fl_root = (uparam.kup_is_root != 0);
681 cred->lc_fl_mds = (uparam.kup_is_mds != 0);
682 cred->lc_tgt_nid = uparam.kup_nid;
683 cred->lc_tgt_svc = uparam.kup_svc;
685 if (lgss_prepare_cred(cred)) {
686 logmsg(LL_ERR, "key %08x: failed to prepare credentials "
687 "for user %d\n", keyid, uparam.kup_uid);
691 /* pre initialize the key. note the keyring linked to is actually of the
692 * original requesting process, not _this_ upcall process. if it's for
693 * root user, don't link to any keyrings because we want fully control
694 * on it, and share it among all root sessions; otherswise link to
697 if (cred->lc_fl_root || cred->lc_fl_mds)
700 inst_keyring = KEY_SPEC_SESSION_KEYRING;
702 if (keyctl_instantiate(keyid, NULL, 0, inst_keyring)) {
703 logmsg(LL_ERR, "instantiate key %08x: %s\n",
704 keyid, strerror(errno));
708 logmsg(LL_TRACE, "instantiated kernel key %08x\n", keyid);
711 * fork a child to do the real gss negotiation
715 logmsg(LL_ERR, "key %08x: can't create child: %s\n",
716 keyid, strerror(errno));
718 } else if (child == 0) {
719 return lgssc_kr_negotiate(keyid, cred, &uparam);
722 logmsg(LL_TRACE, "forked child %d\n", child);