blob: 43255f33d61c35eac84c7912b5e929254ade91da [file] [edit]
/*****************************************************************************\
* auth_common.c - Common authentication utilities implementation
*****************************************************************************
* Copyright (C) SchedMD LLC.
* Copyright Amazon.com Inc. or its affiliates.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include <inttypes.h>
#include <sys/types.h>
#include "slurm/slurm.h"
#include "slurm/slurm_errno.h"
#include "src/common/slurm_xlator.h"
#include "src/common/data.h"
#include "src/common/log.h"
#include "src/common/slurm_protocol_defs.h"
#include "src/common/uid.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/interfaces/serializer.h"
#include "src/plugins/auth/common/auth_common.h"
static identity_t *_extract_identity_from_data(data_t *data_id, uid_t uid,
gid_t gid);
static int _validate_identity_required_fields(identity_t *id)
{
xassert(id);
if (!id->pw_name || !*id->pw_name) {
debug("%s: identity missing required field: name", __func__);
return SLURM_ERROR;
}
if (!id->pw_gecos || !*id->pw_gecos) {
debug("%s: identity missing required field: gecos", __func__);
return SLURM_ERROR;
}
if (!id->pw_dir || (*id->pw_dir != '/')) {
debug("%s: identity missing required field: dir", __func__);
return SLURM_ERROR;
}
if (!id->pw_shell || (*id->pw_shell != '/')) {
debug("%s: identity missing required field: shell", __func__);
return SLURM_ERROR;
}
return SLURM_SUCCESS;
}
extern identity_t *auth_common_extract_identity_from_data(data_t *jwt_data)
{
data_t *id_data = NULL;
int64_t uid_val, gid_val;
uid_t uid = SLURM_AUTH_NOBODY;
gid_t gid = SLURM_AUTH_NOBODY;
identity_t *identity = NULL;
if (!data_get_int_converted(data_key_get(jwt_data, "uid"), &uid_val)) {
if ((uid_val < 0) || (uid_val > UINT32_MAX)) {
debug("%s: JWT has invalid uid value: %ld",
__func__, uid_val);
return NULL;
}
uid = (uid_t) uid_val;
} else {
debug("%s: uid required but not found in JWT", __func__);
return NULL;
}
if (!data_get_int_converted(data_key_get(jwt_data, "gid"), &gid_val)) {
if ((gid_val < 0) || (gid_val > UINT32_MAX)) {
debug("%s: JWT has invalid gid value: %ld",
__func__, gid_val);
return NULL;
}
gid = (gid_t) gid_val;
} else {
debug("%s: gid required but not found in JWT", __func__);
return NULL;
}
if (!(id_data = data_key_get(jwt_data, "id"))) {
debug("%s: no 'id' object found, identity parsing not possible",
__func__);
return NULL;
}
identity = _extract_identity_from_data(id_data, uid, gid);
/* Validate the complete identity */
if (!identity || _validate_identity_required_fields(identity)) {
error("%s: identity validation failed", __func__);
FREE_NULL_IDENTITY(identity);
} else {
debug2("%s: successfully parsed identity for user '%s'",
__func__, identity->pw_name);
}
return identity;
}
extern char *auth_common_get_identity_string(identity_t *id, uid_t uid,
gid_t gid)
{
data_t *data = NULL;
char *json = NULL;
identity_t *id_local = NULL;
if (!id && !(id = id_local = fetch_identity(uid, gid, true)))
return NULL;
data = auth_common_identity_to_data(id);
FREE_NULL_IDENTITY(id_local);
serialize_g_data_to_string(&json, NULL, data, MIME_TYPE_JSON,
SER_FLAGS_COMPACT);
FREE_NULL_DATA(data);
return json;
}
extern data_t *auth_common_identity_to_data(identity_t *id)
{
data_t *data = NULL, *data_id = NULL, *groups = NULL;
data = data_set_dict(data_new());
/*
* Don't bother constructing incomplete identities here,
* none of the relevant fields are set here.
*/
if (!id || id->fake)
return data;
data_id = data_set_dict(data_key_set(data, "id"));
data_set_string(data_key_set(data_id, "name"), id->pw_name);
data_set_string(data_key_set(data_id, "gecos"), id->pw_gecos);
data_set_string(data_key_set(data_id, "dir"), id->pw_dir);
data_set_string(data_key_set(data_id, "shell"), id->pw_shell);
if (id->gr_names) {
groups = data_set_dict(data_key_set(data_id, "groups"));
for (int i = 0; i < id->ngids; i++)
data_set_int(data_key_set(groups, id->gr_names[i]),
id->gids[i]);
} else if (id->ngids) {
data_t *data_gids =
data_set_list(data_key_set(data_id, "gids"));
for (int i = 0; i < id->ngids; i++)
data_set_int(data_list_append(data_gids), id->gids[i]);
}
return data;
}
static data_for_each_cmd_t _for_each_group(const char *key, const data_t *data,
void *arg)
{
identity_t *id = (identity_t *) arg;
int64_t gid_val;
if (data_get_int_converted(data, &gid_val)) {
error("%s: failed to convert group gid", __func__);
return DATA_FOR_EACH_FAIL;
}
if (gid_val < 0 || gid_val > UINT32_MAX) {
error("%s: invalid gid value: %ld", __func__, gid_val);
return DATA_FOR_EACH_FAIL;
}
id->gids[id->ngids] = (gid_t) gid_val;
id->gr_names[id->ngids] = xstrdup(key);
id->ngids++;
return DATA_FOR_EACH_CONT;
}
static data_for_each_cmd_t _for_each_gid(const data_t *data, void *arg)
{
identity_t *id = (identity_t *) arg;
int64_t gid_val;
if (data_get_int_converted(data, &gid_val)) {
error("%s: failed to convert gid", __func__);
return DATA_FOR_EACH_FAIL;
}
if (gid_val < 0 || gid_val > UINT32_MAX) {
error("%s: invalid gid value: %ld", __func__, gid_val);
return DATA_FOR_EACH_FAIL;
}
id->gids[id->ngids] = (gid_t) gid_val;
id->ngids++;
return DATA_FOR_EACH_CONT;
}
extern identity_t *auth_common_extract_identity(char *json, uid_t uid,
gid_t gid)
{
data_t *data_id = NULL;
identity_t *id = NULL;
if (serialize_g_string_to_data(&data_id, json, strlen(json),
MIME_TYPE_JSON)) {
error("%s: failed to decode id field", __func__);
FREE_NULL_IDENTITY(id);
return NULL;
}
id = _extract_identity_from_data(data_id, uid, gid);
FREE_NULL_DATA(data_id);
return id;
}
static identity_t *_extract_identity_from_data(data_t *data_id, uid_t uid,
gid_t gid)
{
data_t *groups = NULL, *gids = NULL;
int ngids;
identity_t *id = xmalloc(sizeof(*id));
id->uid = uid;
id->gid = gid;
id->pw_name = xstrdup(data_get_string(data_key_get(data_id, "name")));
id->pw_gecos = xstrdup(data_get_string(data_key_get(data_id, "gecos")));
id->pw_dir = xstrdup(data_get_string(data_key_get(data_id, "dir")));
id->pw_shell = xstrdup(data_get_string(data_key_get(data_id, "shell")));
if ((groups = data_key_get(data_id, "groups"))) {
ngids = data_get_dict_length(groups);
id->gids = xcalloc(ngids, sizeof(gid_t));
id->gr_names = xcalloc(ngids, sizeof(char *));
if (data_dict_for_each_const(groups, _for_each_group, id) < 0) {
error("%s: data_dict_for_each_const failed", __func__);
FREE_NULL_IDENTITY(id);
return NULL;
}
} else if ((gids = data_key_get(data_id, "gids"))) {
ngids = data_get_list_length(gids);
id->gids = xcalloc(ngids, sizeof(gid_t));
if (data_list_for_each_const(gids, _for_each_gid, id) < 0) {
error("%s: data_list_for_each_const failed", __func__);
FREE_NULL_IDENTITY(id);
return NULL;
}
}
return id;
}