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;
60 gss_OID lnd_mech; /* mech OID */
61 gss_name_t lnd_svc_name; /* service name */
62 u_int lnd_req_flags; /* request flags */
63 gss_cred_id_t lnd_cred; /* credential */
64 gss_ctx_id_t lnd_ctx; /* session context */
65 gss_buffer_desc lnd_rmt_ctx; /* remote handle of context */
66 uint32_t lnd_seq_win; /* sequence window */
73 * context creation response
75 struct lgss_init_res {
76 gss_buffer_desc gr_ctx; /* context handle */
77 u_int gr_major; /* major status */
78 u_int gr_minor; /* minor status */
79 u_int gr_win; /* sequence window */
80 gss_buffer_desc gr_token; /* token */
83 struct keyring_upcall_param {
95 /****************************************
96 * child process: gss negotiation *
97 ****************************************/
99 #define INIT_CHANNEL "/proc/fs/lustre/sptlrpc/gss/init_channel"
101 int do_nego_rpc(struct lgss_nego_data *lnd,
102 gss_buffer_desc *gss_token,
103 struct lgss_init_res *gr)
105 struct lgssd_ioctl_param param;
111 logmsg(LL_TRACE, "start negotiation rpc\n");
113 pw = getpwuid(lnd->lnd_uid);
115 logmsg(LL_ERR, "no uid %u in local user database\n",
120 param.version = GSSD_INTERFACE_VERSION;
121 param.uuid = lnd->lnd_uuid;
122 param.lustre_svc = lnd->lnd_lsvc;
123 param.uid = lnd->lnd_uid;
124 param.gid = pw->pw_gid;
125 param.send_token_size = gss_token->length;
126 param.send_token = (char *) gss_token->value;
127 param.reply_buf_size = sizeof(outbuf);
128 param.reply_buf = outbuf;
130 logmsg(LL_TRACE, "to open " INIT_CHANNEL "\n");
132 fd = open(INIT_CHANNEL, O_WRONLY);
134 logmsg(LL_ERR, "can't open " INIT_CHANNEL "\n");
138 logmsg(LL_TRACE, "to down-write\n");
140 ret = write(fd, ¶m, sizeof(param));
141 if (ret != sizeof(param)) {
142 logmsg(LL_ERR, "lustre ioctl err: %d\n", strerror(errno));
148 logmsg(LL_TRACE, "do_nego_rpc: to parse reply\n");
150 logmsg(LL_ERR, "status: %d (%s)\n",
151 param.status, strerror((int)param.status));
153 /* kernel return -ETIMEDOUT means the rpc timedout, we should
154 * notify the caller to reinitiate the gss negotiation, by
155 * returning -ERESTART
157 if (param.status == -ETIMEDOUT)
163 p = (unsigned int *)outbuf;
169 gr->gr_ctx.length = *p++;
170 gr->gr_ctx.value = malloc(gr->gr_ctx.length);
171 memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
172 p += (((gr->gr_ctx.length + 3) & ~3) / 4);
174 gr->gr_token.length = *p++;
175 gr->gr_token.value = malloc(gr->gr_token.length);
176 memcpy(gr->gr_token.value, p, gr->gr_token.length);
177 p += (((gr->gr_token.length + 3) & ~3) / 4);
179 logmsg(LL_DEBUG, "do_nego_rpc: receive handle len %d, token len %d\n",
180 gr->gr_ctx.length, gr->gr_token.length);
185 * if return error, the lnd_rpc_err or lnd_gss_err is set.
187 int lgssc_negotiation(struct lgss_nego_data *lnd)
189 struct lgss_init_res gr;
190 gss_buffer_desc *recv_tokenp, send_token;
191 OM_uint32 maj_stat, min_stat, ret_flags;
193 logmsg(LL_TRACE, "start gss negotiation\n");
195 /* GSS context establishment loop. */
196 memset(&gr, 0, sizeof(gr));
197 recv_tokenp = GSS_C_NO_BUFFER;
200 maj_stat = gss_init_sec_context(&min_stat,
209 NULL, /* used mech */
212 NULL); /* time rec */
214 if (recv_tokenp != GSS_C_NO_BUFFER) {
215 gss_release_buffer(&min_stat, &gr.gr_token);
216 recv_tokenp = GSS_C_NO_BUFFER;
219 if (maj_stat != GSS_S_COMPLETE &&
220 maj_stat != GSS_S_CONTINUE_NEEDED) {
221 lnd->lnd_gss_err = maj_stat;
223 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
224 "failed init context");
228 if (send_token.length != 0) {
229 memset(&gr, 0, sizeof(gr));
231 lnd->lnd_rpc_err = do_nego_rpc(lnd, &send_token, &gr);
232 gss_release_buffer(&min_stat, &send_token);
234 if (lnd->lnd_rpc_err) {
235 logmsg(LL_ERR, "negotiation rpc error: %d\n",
240 if (gr.gr_major != GSS_S_COMPLETE &&
241 gr.gr_major != GSS_S_CONTINUE_NEEDED) {
242 lnd->lnd_gss_err = gr.gr_major;
244 logmsg(LL_ERR, "negotiation gss error %x\n",
249 if (gr.gr_ctx.length != 0) {
250 if (lnd->lnd_rmt_ctx.value)
251 gss_release_buffer(&min_stat,
253 lnd->lnd_rmt_ctx = gr.gr_ctx;
256 if (gr.gr_token.length != 0) {
257 if (maj_stat != GSS_S_CONTINUE_NEEDED)
259 recv_tokenp = &gr.gr_token;
263 /* GSS_S_COMPLETE => check gss header verifier,
264 * usually checked in gss_validate
266 if (maj_stat == GSS_S_COMPLETE) {
267 lnd->lnd_established = 1;
268 lnd->lnd_seq_win = gr.gr_win;
273 /* End context negotiation loop. */
274 if (!lnd->lnd_established) {
275 if (gr.gr_token.length != 0)
276 gss_release_buffer(&min_stat, &gr.gr_token);
278 if (lnd->lnd_gss_err == GSS_S_COMPLETE)
279 lnd->lnd_rpc_err = -EACCES;
281 logmsg(LL_ERR, "context negotiation failed\n");
285 logmsg(LL_DEBUG, "successfully negotiated a context\n");
290 * if return error, the lnd_rpc_err or lnd_gss_err is set.
292 int lgssc_init_nego_data(struct lgss_nego_data *lnd,
293 struct keyring_upcall_param *kup,
296 gss_buffer_desc sname;
297 OM_uint32 maj_stat, min_stat;
299 memset(lnd, 0, sizeof(*lnd));
301 lnd->lnd_uid = kup->kup_uid;
302 lnd->lnd_lsvc = kup->kup_svc;
303 lnd->lnd_uuid = kup->kup_tgt;
305 lnd->lnd_established = 0;
306 lnd->lnd_svc_name = GSS_C_NO_NAME;
307 lnd->lnd_cred = GSS_C_NO_CREDENTIAL;
308 lnd->lnd_ctx = GSS_C_NO_CONTEXT;
309 lnd->lnd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
310 lnd->lnd_seq_win = 0;
314 lnd->lnd_mech = (gss_OID) &krb5oid;
315 lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG;
318 logmsg(LL_ERR, "invalid mech: %d\n", mech);
319 lnd->lnd_rpc_err = -EACCES;
323 sname.value = g_service;
324 sname.length = strlen(g_service);
326 maj_stat = gss_import_name(&min_stat, &sname,
327 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
329 if (maj_stat != GSS_S_COMPLETE) {
330 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
331 "can't import svc name");
332 lnd->lnd_gss_err = maj_stat;
339 void lgssc_fini_nego_data(struct lgss_nego_data *lnd)
341 OM_uint32 maj_stat, min_stat;
343 if (lnd->lnd_svc_name != GSS_C_NO_NAME) {
344 maj_stat = gss_release_name(&min_stat, &lnd->lnd_svc_name);
345 if (maj_stat != GSS_S_COMPLETE)
346 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
347 "can't release service name");
350 if (lnd->lnd_cred != GSS_C_NO_CREDENTIAL) {
351 maj_stat = gss_release_cred(&min_stat, &lnd->lnd_cred);
352 if (maj_stat != GSS_S_COMPLETE)
353 logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
354 "can't release credential");
359 int error_kernel_key(key_serial_t keyid, int rpc_error, int gss_error)
365 logmsg(LL_TRACE, "revoking kernel key %08x\n", keyid);
368 end = buf + sizeof(buf);
370 WRITE_BYTES(&p, end, seqwin);
371 WRITE_BYTES(&p, end, rpc_error);
372 WRITE_BYTES(&p, end, gss_error);
375 if (keyctl_update(keyid, buf, p - buf)) {
376 if (errno != EAGAIN) {
377 logmsg(LL_ERR, "revoke key %08x: %s\n",
378 keyid, strerror(errno));
382 logmsg(LL_WARN, "key %08x: revoking too soon, try again\n",
388 logmsg(LL_INFO, "key %08x: revoked\n", keyid);
393 int update_kernel_key(key_serial_t keyid,
394 struct lgss_nego_data *lnd,
395 gss_buffer_desc *ctx_token)
397 char *buf = NULL, *p = NULL, *end = NULL;
398 unsigned int buf_size = 0;
401 logmsg(LL_TRACE, "updating kernel key %08x\n", keyid);
403 buf_size = sizeof(lnd->lnd_seq_win) +
404 sizeof(lnd->lnd_rmt_ctx.length) + lnd->lnd_rmt_ctx.length +
405 sizeof(ctx_token->length) + ctx_token->length;
406 buf = malloc(buf_size);
408 logmsg(LL_ERR, "key %08x: can't alloc update buf: size %d\n",
414 end = buf + buf_size;
417 if (WRITE_BYTES(&p, end, lnd->lnd_seq_win))
419 if (write_buffer(&p, end, &lnd->lnd_rmt_ctx))
421 if (write_buffer(&p, end, ctx_token))
425 if (keyctl_update(keyid, buf, p - buf)) {
426 if (errno != EAGAIN) {
427 logmsg(LL_ERR, "update key %08x: %s\n",
428 keyid, strerror(errno));
432 logmsg(LL_DEBUG, "key %08x: updating too soon, try again\n",
439 logmsg(LL_DEBUG, "key %08x: updated\n", keyid);
446 * note we can't assume authority in child process
448 int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred,
449 struct keyring_upcall_param *kup)
451 struct lgss_nego_data lnd;
452 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
456 logmsg(LL_TRACE, "child start on behalf of key %08x: "
457 "cred %p, uid %u, svc %u, nid %Lx\n", keyid, cred,
458 cred->lc_uid, cred->lc_tgt_svc, cred->lc_tgt_nid);
460 if (kup->kup_gid != 0 && setregid(kup->kup_gid, kup->kup_gid)) {
461 logmsg(LL_WARN, "key %08x, failed set gids to %u: %s\n",
462 keyid, kup->kup_gid, strerror(errno));
465 if (kup->kup_uid != 0 && setreuid(kup->kup_uid, kup->kup_uid)) {
466 logmsg(LL_WARN, "key %08x, failed set uids to %u: %s\n",
467 keyid, kup->kup_uid, strerror(errno));
471 * link to session keyring, allow the key to be found.
473 if (keyctl_link(keyid, KEY_SPEC_SESSION_KEYRING)) {
474 logmsg(LL_ERR, "key %08x, failed to link to session "
475 "keyring: %s\n", keyid, strerror(errno));
476 error_kernel_key(keyid, -EACCES, 0);
480 if (lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid)) {
481 logmsg(LL_ERR, "key %08x: failed to construct service "
483 error_kernel_key(keyid, -EACCES, 0);
487 if (lgss_using_cred(cred)) {
488 logmsg(LL_ERR, "key %08x: can't using cred\n", keyid);
489 error_kernel_key(keyid, -EACCES, 0);
493 if (lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n)) {
494 logmsg(LL_ERR, "key %08x: failed to initialize "
495 "negotiation data\n", keyid);
496 error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
500 rc = lgssc_negotiation(&lnd);
502 logmsg(LL_ERR, "key %08x: failed to negotiation\n", keyid);
503 error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
507 rc = serialize_context_for_kernel(lnd.lnd_ctx, &token, lnd.lnd_mech);
509 logmsg(LL_ERR, "key %08x: failed to export context\n", keyid);
510 error_kernel_key(keyid, rc, lnd.lnd_gss_err);
514 rc = update_kernel_key(keyid, &lnd, &token);
519 logmsg(LL_INFO, "key %08x for user %u is updated OK!\n",
520 keyid, kup->kup_uid);
522 if (token.length != 0)
523 gss_release_buffer(&min_stat, &token);
525 lgssc_fini_nego_data(&lnd);
528 if (keyctl_unlink(keyid, KEY_SPEC_SESSION_KEYRING)) {
529 logmsg(LL_WARN, "failed to unlink key %08x: %s\n",
530 keyid, strerror(errno));
534 lgss_release_cred(cred);
539 * call out info format: s[:s]...
540 * [0]: mech_name (string)
541 * [1]: flags (chars) FMT: r-root; m-mds
542 * [2]: lustre_svc (uint)
543 * [3]: target_nid (uint64)
544 * [4]: target_uuid (string)
547 int parse_callout_info(const char *coinfo,
548 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 < 4; i++) {
564 pos = strchr(string, ':');
566 logmsg(LL_ERR, "short of components\n");
576 logmsg(LL_TRACE, "components: %s,%s,%s,%s,%s\n",
577 data[0], data[1], data[2], data[3], data[4], data[5]);
579 strncpy(uparam->kup_mech, data[0], sizeof(uparam->kup_mech));
580 if (strchr(data[1], 'r'))
581 uparam->kup_is_root = 1;
582 if (strchr(data[1], 'm'))
583 uparam->kup_is_mds = 1;
584 uparam->kup_svc = strtol(data[2], NULL, 0);
585 uparam->kup_nid = strtoll(data[3], NULL, 0);
586 strncpy(uparam->kup_tgt, data[4], sizeof(uparam->kup_tgt));
588 logmsg(LL_DEBUG, "parse call out info: mech %s, is_root %d, "
589 "is_mds %d, svc %d, nid 0x%Lx, tgt %s\n",
590 uparam->kup_mech, uparam->kup_is_root, uparam->kup_is_mds,
591 uparam->kup_svc, uparam->kup_nid, uparam->kup_tgt);
595 /****************************************
597 ****************************************/
599 int main(int argc, char *argv[])
601 struct keyring_upcall_param uparam;
604 key_serial_t inst_keyring;
606 struct lgss_mech_type *mech;
607 struct lgss_cred *cred;
610 * parse & sanity check upcall parameters
611 * expected to be called with:
615 * [4]: key description
619 * [8]: thread keyring
620 * [9]: process keyring
621 * [10]: session keyring
623 if (argc != 10 + 1) {
624 logmsg(LL_ERR, "invalid parameter number %d\n", argc);
628 logmsg(LL_INFO, "key %s, desc %s, uid %s, sring %s, coinfo %s\n",
629 argv[2], argv[4], argv[6], argv[10], argv[5]);
631 memset(&uparam, 0, sizeof(uparam));
633 if (strcmp(argv[1], "create") != 0) {
634 logmsg(LL_ERR, "invalid OP %s\n", argv[1]);
638 if (sscanf(argv[2], "%d", &keyid) != 1) {
639 logmsg(LL_ERR, "can't extract KeyID: %s\n", argv[2]);
643 if (sscanf(argv[6], "%d", &uparam.kup_uid) != 1) {
644 logmsg(LL_ERR, "can't extract UID: %s\n", argv[6]);
648 if (sscanf(argv[10], "%d", &sring) != 1) {
649 logmsg(LL_ERR, "can't extract session keyring: %s\n", argv[10]);
653 if (parse_callout_info(argv[5], &uparam)) {
654 logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]);
658 logmsg(LL_TRACE, "parsing parameters OK\n");
663 mech = lgss_name2mech(uparam.kup_mech);
665 logmsg(LL_ERR, "key %08x: unsupported mech: %s\n",
666 keyid, uparam.kup_mech);
670 if (lgss_mech_initialize(mech)) {
671 logmsg(LL_ERR, "key %08x: can't initialize mech %s\n",
672 keyid, mech->lmt_name);
676 cred = lgss_create_cred(mech);
678 logmsg(LL_ERR, "key %08x: can't create a new %s cred\n",
679 keyid, mech->lmt_name);
683 cred->lc_uid = uparam.kup_uid;
684 cred->lc_fl_root = (uparam.kup_is_root != 0);
685 cred->lc_fl_mds = (uparam.kup_is_mds != 0);
686 cred->lc_tgt_nid = uparam.kup_nid;
687 cred->lc_tgt_svc = uparam.kup_svc;
689 if (lgss_prepare_cred(cred)) {
690 logmsg(LL_ERR, "key %08x: failed to prepare credentials "
691 "for user %d\n", keyid, uparam.kup_uid);
696 * pre initialize the key
698 inst_keyring = (cred->lc_fl_root || cred->lc_fl_mds) ?
699 0 : KEY_SPEC_SESSION_KEYRING;
701 if (keyctl_instantiate(keyid, NULL, 0, inst_keyring)) {
702 logmsg(LL_ERR, "instantiate key %08x: %s\n",
703 keyid, strerror(errno));
707 logmsg(LL_TRACE, "instantiated kernel key %08x\n", keyid);
710 * fork a child to do the real gss negotiation
714 logmsg(LL_ERR, "key %08x: can't create child: %s\n",
715 keyid, strerror(errno));
717 } else if (child == 0) {
718 return lgssc_kr_negotiate(keyid, cred, &uparam);
721 logmsg(LL_TRACE, "forked child %d\n", child);