Viewing: svcgssd.c
/*
* gssd.c
*
* Copyright (c) 2000 The Regents of the University of Michigan.
* All rights reserved.
*
* Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
* Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
* Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
* Copyright (c) 2002 J. Bruce Fields <bfields@UMICH.EDU>.
*
* Copyright (c) 2016, Intel Corporation.
*
* All rights reserved, all wrongs reversed.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <dirent.h>
#include "svcgssd.h"
#include "gss_util.h"
#include "err_util.h"
#include "lsupport.h"
#include <linux/lustre/lustre_ver.h>
int null_enabled;
int krb_enabled;
int sk_enabled;
int srv_ioc_dev = -1;
static void
closeall(int min)
{
DIR *dir = opendir("/proc/self/fd");
if (dir != NULL) {
int dfd = dirfd(dir);
struct dirent *d;
while ((d = readdir(dir)) != NULL) {
char *endp;
long n = strtol(d->d_name, &endp, 10);
if (*endp != '\0' && n >= min && n != dfd)
(void) close(n);
}
closedir(dir);
} else {
int fd = sysconf(_SC_OPEN_MAX);
while (--fd >= min)
(void) close(fd);
}
}
/*
* mydaemon creates a pipe between the partent and child
* process. The parent process will wait until the
* child dies or writes a '1' on the pipe signaling
* that it started successfully.
*/
int pipefds[2] = { -1, -1};
static void
mydaemon(int nochdir, int noclose)
{
int pid, status, tempfd;
if (pipe(pipefds) < 0) {
printerr(LL_ERR, "%s: pipe() failed: errno %d (%s)\n",
__func__, errno, strerror(errno));
exit(1);
}
if ((pid = fork ()) < 0) {
printerr(LL_ERR, "%s: fork() failed: errno %d (%s)\n",
__func__, errno, strerror(errno));
exit(1);
}
if (pid != 0) {
/*
* Parent. Wait for status from child.
*/
close(pipefds[1]);
if (read(pipefds[0], &status, 1) != 1)
exit(1);
exit (0);
}
/* Child. */
close(pipefds[0]);
setsid ();
if (nochdir == 0) {
if (chdir ("/") == -1) {
printerr(LL_ERR,
"%s: chdir() failed: errno %d (%s)\n",
__func__, errno, strerror(errno));
exit(1);
}
}
while (pipefds[1] <= 2) {
pipefds[1] = dup(pipefds[1]);
if (pipefds[1] < 0) {
printerr(LL_ERR, "%s: dup() failed: errno %d (%s)\n",
__func__, errno, strerror(errno));
exit(1);
}
}
if (noclose == 0) {
tempfd = open("/dev/null", O_RDWR);
if (tempfd < 0) {
printerr(LL_ERR, "%s: open() failed: errno %d (%s)\n",
__func__, errno, strerror(errno));
exit(1);
}
dup2(tempfd, 0);
dup2(tempfd, 1);
dup2(tempfd, 2);
closeall(3);
}
}
static void
release_parent()
{
int status;
ssize_t sret __attribute__ ((unused));
if (pipefds[1] > 0) {
sret = write(pipefds[1], &status, 1);
close(pipefds[1]);
pipefds[1] = -1;
}
}
static void
sig_die(int signal)
{
/* cleanup allocated strings for realms */
gssd_cleanup_realms();
/* remove socket */
unlink(GSS_SOCKET_PATH);
printerr(LL_WARN, "exiting on signal %d\n", signal);
if (signal == SIGTERM)
exit(EXIT_SUCCESS);
else
exit(EXIT_FAILURE);
}
static void
sig_hup(int signal)
{
/* don't exit on SIGHUP */
printerr(LL_WARN, "Received SIGHUP... Ignoring.\n");
}
int set_srv_ioc_dev(void)
{
struct obd_ioctl_data data;
char rawbuf[MAX_IOC_BUFLEN];
char *buf = rawbuf;
char target[4];
int rc;
if (is_mgs())
strncpy(target, "MGS", sizeof(target));
else if (is_mds())
strncpy(target, "MDS", sizeof(target));
else if (is_oss())
strncpy(target, "OSS", sizeof(target));
else
return -EINVAL;
memset(&data, 0, sizeof(data));
data.ioc_dev = -1;
data.ioc_inllen1 = strlen(target) + 1;
data.ioc_inlbuf1 = target;
memset(buf, 0, sizeof(rawbuf));
rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
if (rc < 0) {
printerr(LL_ERR, "invalid ioctl input: %s\n", strerror(-rc));
return rc;
}
rc = l_ioctl(OBD_DEV_ID, OBD_IOC_NAME2DEV, buf);
if (rc < 0) {
rc = -errno;
printerr(LL_ERR, "invalid ioctl: %s\n", strerror(errno));
return rc;
}
rc = llapi_ioctl_unpack(&data, buf, sizeof(rawbuf));
if (rc < 0) {
printerr(LL_ERR, "invalid ioctl output: %s\n", strerror(-rc));
return rc;
}
srv_ioc_dev = data.ioc_dev;
return 0;
}
static void
usage(FILE *fp, char *progname)
{
fprintf(fp, "usage: %s [ -fnvmogkRsz ]\n",
progname);
fprintf(stderr, "-f - Run in foreground\n");
fprintf(stderr, "-g - Service MGS\n");
fprintf(stderr, "-k - Enable kerberos support\n");
fprintf(stderr, "-m - Service MDS\n");
fprintf(stderr, "-n - Don't establish kerberos credentials\n");
fprintf(stderr, "-o - Service OSS\n");
fprintf(stderr, "-R REALM - Kerberos Realm to use, instead of default\n");
#ifdef HAVE_OPENSSL_SSK
fprintf(stderr, "-s - Enable shared secret key support\n");
#endif
fprintf(stderr, "-v - Verbosity\n");
fprintf(stderr, "-z - Enable gssnull support\n");
exit(fp == stderr);
}
int main(int argc, char *argv[])
{
int get_creds = 1;
int fg = 0;
int verbosity = 0;
int opt;
int must_srv_mds = 0, must_srv_oss = 0, must_srv_mgs = 0;
char *realm = NULL;
char *progname;
int ret;
while ((opt = getopt(argc, argv, "fgkmnoR:svz")) != -1) {
switch (opt) {
case 'f':
fg = 1;
break;
case 'g':
get_creds = 1;
must_srv_mgs = 1;
break;
case 'h':
usage(stdout, argv[0]);
break;
case 'k':
krb_enabled = 1;
break;
case 'm':
get_creds = 1;
must_srv_mds = 1;
break;
case 'n':
get_creds = 0;
break;
case 'o':
get_creds = 1;
must_srv_oss = 1;
break;
case 'R':
realm = optarg;
break;
case 's':
#ifdef HAVE_OPENSSL_SSK
sk_enabled = 1;
#else
fprintf(stderr, "error: request for SSK but service "
"support not enabled\n");
usage(stderr, argv[0]);
#endif
break;
case 'v':
verbosity++;
break;
case 'z':
null_enabled = 1;
break;
default:
usage(stderr, argv[0]);
break;
}
}
if ((progname = strrchr(argv[0], '/')))
progname++;
else
progname = argv[0];
if (!sk_enabled && !krb_enabled && !null_enabled) {
#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 0, 53, 0)
fprintf(stderr, "warning: no -k, -s, or -z option given, "
"assume -k for backward compatibility\n");
krb_enabled = 1;
#else
fprintf(stderr, "error: need one of -k, -s, or -z options\n");
usage(stderr, argv[0]);
#endif
}
if (realm && !krb_enabled) {
fprintf(stderr, "error: need -k option if -R is used\n");
usage(stderr, argv[0]);
}
initerr(progname, verbosity, fg);
llapi_register_ioc_dev(OBD_DEV_ID, OBD_DEV_PATH);
ret = set_srv_ioc_dev();
if (ret < 0)
printerr(LL_INFO, "Could not set ioc dev: %s\n",
strerror(-ret));
/* do not keep OBD_DEV_ID open for the whole lifetime of this daemon */
llapi_unregister_ioc_dev(OBD_DEV_ID);
/* For kerberos use gss mechanisms but ignore for sk and null */
if (krb_enabled) {
if (gssd_check_mechs()) {
printerr(LL_ERR,
"ERROR: problem with gssapi library\n");
ret = -1;
goto err_krb;
}
ret = gss_get_realm(realm);
if (ret) {
printerr(LL_ERR, "ERROR: no Kerberos realm: %s\n",
error_message(ret));
goto err_krb;
}
printerr(LL_WARN, "Kerberos realm: %s\n", krb5_this_realm);
if (get_creds &&
gssd_prepare_creds(must_srv_mgs, must_srv_mds,
must_srv_oss)) {
printerr(LL_ERR,
"unable to obtain root (machine) credentials\n");
printerr(LL_ERR,
"do you have a keytab entry for <lustre_xxs>/<your.host>@<YOUR.REALM> in /etc/krb5.keytab?\n");
ret = -1;
goto err_krb;
}
err_krb:
if (ret) {
krb_enabled = 0;
printerr(LL_ERR, "ERROR: disabling Kerberos support\n");
if (!sk_enabled && !krb_enabled && !null_enabled)
exit(EXIT_FAILURE);
}
}
if (!fg)
mydaemon(0, 0);
/*
* XXX: There is risk of memory leak for missing call
* cleanup_mapping() for SIGKILL and SIGSTOP.
*/
signal(SIGINT, sig_die);
signal(SIGTERM, sig_die);
signal(SIGHUP, sig_hup);
if (!fg)
release_parent();
gssd_init_unique(GSSD_SVC);
svcgssd_run();
gssd_cleanup_realms();
printerr(LL_ERR, "svcgssd_run returned!\n");
abort();
}