/* * viridentity.c: helper APIs for managing user identities * * Copyright (C) 2012-2013 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; If not, see * . * */ #include #include #if WITH_SELINUX # include #endif #include "internal.h" #include "viralloc.h" #include "virerror.h" #include "viridentity.h" #include "virlog.h" #include "virobject.h" #include "virthread.h" #include "virutil.h" #include "virstring.h" #include "virprocess.h" #include "virtypedparam.h" #define VIR_FROM_THIS VIR_FROM_IDENTITY VIR_LOG_INIT("util.identity"); struct _virIdentity { GObject parent; int nparams; int maxparams; virTypedParameterPtr params; }; G_DEFINE_TYPE(virIdentity, vir_identity, G_TYPE_OBJECT) static virThreadLocal virIdentityCurrent; static void virIdentityFinalize(GObject *obj); static void virIdentityCurrentCleanup(void *ident) { if (ident) g_object_unref(ident); } static int virIdentityOnceInit(void) { if (virThreadLocalInit(&virIdentityCurrent, virIdentityCurrentCleanup) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot initialize thread local for current identity")); return -1; } return 0; } VIR_ONCE_GLOBAL_INIT(virIdentity); static void vir_identity_init(virIdentity *ident G_GNUC_UNUSED) { } static void vir_identity_class_init(virIdentityClass *klass) { GObjectClass *obj = G_OBJECT_CLASS(klass); obj->finalize = virIdentityFinalize; } /** * virIdentityGetCurrent: * * Get the current identity associated with this thread. The * caller will own a reference to the returned identity, but * must not modify the object in any way, other than to * release the reference when done with g_object_unref * * Returns: a reference to the current identity, or NULL */ virIdentityPtr virIdentityGetCurrent(void) { virIdentityPtr ident; if (virIdentityInitialize() < 0) return NULL; ident = virThreadLocalGet(&virIdentityCurrent); if (ident) g_object_ref(ident); return ident; } /** * virIdentitySetCurrent: * * Set the new identity to be associated with this thread. * The caller should not modify the passed identity after * it has been set, other than to release its own reference. * * Returns 0 on success, or -1 on error */ int virIdentitySetCurrent(virIdentityPtr ident) { g_autoptr(virIdentity) old = NULL; if (virIdentityInitialize() < 0) return -1; old = virThreadLocalGet(&virIdentityCurrent); if (virThreadLocalSet(&virIdentityCurrent, ident ? g_object_ref(ident) : NULL) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to set thread local identity")); if (ident) g_object_unref(ident); return -1; } return 0; } /** * virIdentityGetSystem: * * Returns an identity that represents the system itself. * This is the identity that the process is running as * * Returns a reference to the system identity, or NULL */ virIdentityPtr virIdentityGetSystem(void) { g_autofree char *username = NULL; g_autofree char *groupname = NULL; unsigned long long startTime; g_autoptr(virIdentity) ret = NULL; #if WITH_SELINUX char *con; #endif if (!(ret = virIdentityNew())) return NULL; if (virIdentitySetProcessID(ret, getpid()) < 0) return NULL; if (virProcessGetStartTime(getpid(), &startTime) < 0) return NULL; if (startTime != 0 && virIdentitySetProcessTime(ret, startTime) < 0) return NULL; if (!(username = virGetUserName(geteuid()))) return ret; if (virIdentitySetUserName(ret, username) < 0) return NULL; if (virIdentitySetUNIXUserID(ret, getuid()) < 0) return NULL; if (!(groupname = virGetGroupName(getegid()))) return ret; if (virIdentitySetGroupName(ret, groupname) < 0) return NULL; if (virIdentitySetUNIXGroupID(ret, getgid()) < 0) return NULL; #if WITH_SELINUX if (is_selinux_enabled() > 0) { if (getcon(&con) < 0) { virReportSystemError(errno, "%s", _("Unable to lookup SELinux process context")); return NULL; } if (virIdentitySetSELinuxContext(ret, con) < 0) { freecon(con); return NULL; } freecon(con); } #endif return g_steal_pointer(&ret); } /** * virIdentityNew: * * Creates a new empty identity object. After creating, one or * more identifying attributes should be set on the identity. * * Returns: a new empty identity */ virIdentityPtr virIdentityNew(void) { return VIR_IDENTITY(g_object_new(VIR_TYPE_IDENTITY, NULL)); } static void virIdentityFinalize(GObject *object) { virIdentityPtr ident = VIR_IDENTITY(object); virTypedParamsFree(ident->params, ident->nparams); G_OBJECT_CLASS(vir_identity_parent_class)->finalize(object); } /* * Returns: 0 if not present, 1 if present, -1 on error */ int virIdentityGetUserName(virIdentityPtr ident, const char **username) { *username = NULL; return virTypedParamsGetString(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_USER_NAME, username); } /* * Returns: 0 if not present, 1 if present, -1 on error */ int virIdentityGetUNIXUserID(virIdentityPtr ident, uid_t *uid) { unsigned long long val; int rc; *uid = -1; rc = virTypedParamsGetULLong(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_UNIX_USER_ID, &val); if (rc <= 0) return rc; *uid = (uid_t)val; return 1; } /* * Returns: 0 if not present, 1 if present, -1 on error */ int virIdentityGetGroupName(virIdentityPtr ident, const char **groupname) { *groupname = NULL; return virTypedParamsGetString(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_GROUP_NAME, groupname); } /* * Returns: 0 if not present, 1 if present, -1 on error */ int virIdentityGetUNIXGroupID(virIdentityPtr ident, gid_t *gid) { unsigned long long val; int rc; *gid = -1; rc = virTypedParamsGetULLong(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_UNIX_GROUP_ID, &val); if (rc <= 0) return rc; *gid = (gid_t)val; return 1; } /* * Returns: 0 if not present, 1 if present, -1 on error */ int virIdentityGetProcessID(virIdentityPtr ident, pid_t *pid) { long long val; int rc; *pid = 0; rc = virTypedParamsGetLLong(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_PROCESS_ID, &val); if (rc <= 0) return rc; *pid = (pid_t)val; return 1; } /* * Returns: 0 if not present, 1 if present, -1 on error */ int virIdentityGetProcessTime(virIdentityPtr ident, unsigned long long *timestamp) { *timestamp = 0; return virTypedParamsGetULLong(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_PROCESS_TIME, timestamp); } /* * Returns: 0 if not present, 1 if present, -1 on error */ int virIdentityGetSASLUserName(virIdentityPtr ident, const char **username) { *username = NULL; return virTypedParamsGetString(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_SASL_USER_NAME, username); } /* * Returns: 0 if not present, 1 if present, -1 on error */ int virIdentityGetX509DName(virIdentityPtr ident, const char **dname) { *dname = NULL; return virTypedParamsGetString(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_X509_DISTINGUISHED_NAME, dname); } /* * Returns: 0 if not present, 1 if present, -1 on error */ int virIdentityGetSELinuxContext(virIdentityPtr ident, const char **context) { *context = NULL; return virTypedParamsGetString(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_SELINUX_CONTEXT, context); } int virIdentitySetUserName(virIdentityPtr ident, const char *username) { if (virTypedParamsGet(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_USER_NAME)) { virReportError(VIR_ERR_OPERATION_DENIED, "%s", _("Identity attribute is already set")); return -1; } return virTypedParamsAddString(&ident->params, &ident->nparams, &ident->maxparams, VIR_CONNECT_IDENTITY_USER_NAME, username); } int virIdentitySetUNIXUserID(virIdentityPtr ident, uid_t uid) { if (virTypedParamsGet(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_UNIX_USER_ID)) { virReportError(VIR_ERR_OPERATION_DENIED, "%s", _("Identity attribute is already set")); return -1; } return virTypedParamsAddULLong(&ident->params, &ident->nparams, &ident->maxparams, VIR_CONNECT_IDENTITY_UNIX_USER_ID, uid); } int virIdentitySetGroupName(virIdentityPtr ident, const char *groupname) { if (virTypedParamsGet(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_GROUP_NAME)) { virReportError(VIR_ERR_OPERATION_DENIED, "%s", _("Identity attribute is already set")); return -1; } return virTypedParamsAddString(&ident->params, &ident->nparams, &ident->maxparams, VIR_CONNECT_IDENTITY_GROUP_NAME, groupname); } int virIdentitySetUNIXGroupID(virIdentityPtr ident, gid_t gid) { if (virTypedParamsGet(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_UNIX_GROUP_ID)) { virReportError(VIR_ERR_OPERATION_DENIED, "%s", _("Identity attribute is already set")); return -1; } return virTypedParamsAddULLong(&ident->params, &ident->nparams, &ident->maxparams, VIR_CONNECT_IDENTITY_UNIX_GROUP_ID, gid); } int virIdentitySetProcessID(virIdentityPtr ident, pid_t pid) { if (virTypedParamsGet(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_PROCESS_ID)) { virReportError(VIR_ERR_OPERATION_DENIED, "%s", _("Identity attribute is already set")); return -1; } return virTypedParamsAddLLong(&ident->params, &ident->nparams, &ident->maxparams, VIR_CONNECT_IDENTITY_PROCESS_ID, pid); } int virIdentitySetProcessTime(virIdentityPtr ident, unsigned long long timestamp) { if (virTypedParamsGet(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_PROCESS_TIME)) { virReportError(VIR_ERR_OPERATION_DENIED, "%s", _("Identity attribute is already set")); return -1; } return virTypedParamsAddULLong(&ident->params, &ident->nparams, &ident->maxparams, VIR_CONNECT_IDENTITY_PROCESS_TIME, timestamp); } int virIdentitySetSASLUserName(virIdentityPtr ident, const char *username) { if (virTypedParamsGet(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_SASL_USER_NAME)) { virReportError(VIR_ERR_OPERATION_DENIED, "%s", _("Identity attribute is already set")); return -1; } return virTypedParamsAddString(&ident->params, &ident->nparams, &ident->maxparams, VIR_CONNECT_IDENTITY_SASL_USER_NAME, username); } int virIdentitySetX509DName(virIdentityPtr ident, const char *dname) { if (virTypedParamsGet(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_X509_DISTINGUISHED_NAME)) { virReportError(VIR_ERR_OPERATION_DENIED, "%s", _("Identity attribute is already set")); return -1; } return virTypedParamsAddString(&ident->params, &ident->nparams, &ident->maxparams, VIR_CONNECT_IDENTITY_X509_DISTINGUISHED_NAME, dname); } int virIdentitySetSELinuxContext(virIdentityPtr ident, const char *context) { if (virTypedParamsGet(ident->params, ident->nparams, VIR_CONNECT_IDENTITY_SELINUX_CONTEXT)) { virReportError(VIR_ERR_OPERATION_DENIED, "%s", _("Identity attribute is already set")); return -1; } return virTypedParamsAddString(&ident->params, &ident->nparams, &ident->maxparams, VIR_CONNECT_IDENTITY_SELINUX_CONTEXT, context); } int virIdentitySetParameters(virIdentityPtr ident, virTypedParameterPtr params, int nparams) { if (virTypedParamsValidate(params, nparams, VIR_CONNECT_IDENTITY_USER_NAME, VIR_TYPED_PARAM_STRING, VIR_CONNECT_IDENTITY_UNIX_USER_ID, VIR_TYPED_PARAM_ULLONG, VIR_CONNECT_IDENTITY_GROUP_NAME, VIR_TYPED_PARAM_STRING, VIR_CONNECT_IDENTITY_UNIX_GROUP_ID, VIR_TYPED_PARAM_ULLONG, VIR_CONNECT_IDENTITY_PROCESS_ID, VIR_TYPED_PARAM_LLONG, VIR_CONNECT_IDENTITY_PROCESS_TIME, VIR_TYPED_PARAM_ULLONG, VIR_CONNECT_IDENTITY_SASL_USER_NAME, VIR_TYPED_PARAM_STRING, VIR_CONNECT_IDENTITY_X509_DISTINGUISHED_NAME, VIR_TYPED_PARAM_STRING, VIR_CONNECT_IDENTITY_SELINUX_CONTEXT, VIR_TYPED_PARAM_STRING, NULL) < 0) return -1; virTypedParamsFree(ident->params, ident->nparams); ident->params = NULL; ident->nparams = 0; ident->maxparams = 0; if (virTypedParamsCopy(&ident->params, params, nparams) < 0) return -1; ident->nparams = nparams; ident->maxparams = nparams; return 0; } int virIdentityGetParameters(virIdentityPtr ident, virTypedParameterPtr *params, int *nparams) { *params = NULL; *nparams = 0; if (virTypedParamsCopy(params, ident->params, ident->nparams) < 0) return -1; *nparams = ident->nparams; return 0; }