| /*****************************************************************************\ |
| * src/common/slurm_cred.c - SLURM job and sbcast credential functions |
| ***************************************************************************** |
| * Copyright (C) 2002-2007 The Regents of the University of California. |
| * Copyright (C) 2008-2010 Lawrence Livermore National Security. |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). |
| * Written by Morris Jette <jette1@llnl.gov>. |
| * CODE-OCEC-09-009. All rights reserved. |
| * |
| * This file is part of SLURM, a resource management program. |
| * For details, see <http://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. |
| \*****************************************************************************/ |
| |
| #ifdef HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| |
| #include <fcntl.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <sys/time.h> |
| |
| #if WITH_PTHREADS |
| # include <pthread.h> |
| #endif /* WITH_PTHREADS */ |
| |
| #include "slurm/slurm_errno.h" |
| #include "src/common/bitstring.h" |
| #include "src/common/gres.h" |
| #include "src/common/io_hdr.h" |
| #include "src/common/job_resources.h" |
| #include "src/common/list.h" |
| #include "src/common/log.h" |
| #include "src/common/macros.h" |
| #include "src/common/plugin.h" |
| #include "src/common/plugrack.h" |
| #include "src/common/slurm_cred.h" |
| #include "src/common/slurm_protocol_api.h" |
| #include "src/common/xassert.h" |
| #include "src/common/xmalloc.h" |
| #include "src/common/xstring.h" |
| |
| #ifndef __sbcast_cred_t_defined |
| # define __sbcast_cred_t_defined |
| typedef struct sbcast_cred sbcast_cred_t; /* opaque data type */ |
| #endif |
| |
| /* |
| * Default credential information expiration window. |
| * Long enough for loading user environment, running prolog, |
| * and dealing with the slurmd getting paged out of memory. |
| */ |
| #define DEFAULT_EXPIRATION_WINDOW 1200 |
| |
| #define EXTREME_DEBUG 0 |
| #define MAX_TIME 0x7fffffff |
| |
| /* |
| * slurm job credential state |
| * |
| */ |
| typedef struct { |
| time_t ctime; /* Time that the cred was created */ |
| time_t expiration; /* Time at which cred is no longer good */ |
| uint32_t jobid; /* SLURM job id for this credential */ |
| uint32_t stepid; /* SLURM step id for this credential */ |
| } cred_state_t; |
| |
| /* |
| * slurm job state information |
| * tracks jobids for which all future credentials have been revoked |
| * |
| */ |
| typedef struct { |
| time_t ctime; /* Time that this entry was created */ |
| time_t expiration; /* Time at which credentials can be purged */ |
| uint32_t jobid; /* SLURM job id for this credential */ |
| time_t revoked; /* Time at which credentials were revoked */ |
| } job_state_t; |
| |
| |
| /* |
| * Completion of slurm credential context |
| */ |
| enum ctx_type { |
| SLURM_CRED_CREATOR, |
| SLURM_CRED_VERIFIER |
| }; |
| |
| /* |
| * slurm sbcast credential state |
| * |
| */ |
| struct sbcast_cred { |
| time_t ctime; /* Time that the cred was created */ |
| time_t expiration; /* Time at which cred is no longer good*/ |
| uint32_t jobid; /* SLURM job id for this credential */ |
| char * nodes; /* nodes for which credential is valid */ |
| char *signature; /* credential signature */ |
| unsigned int siglen; /* signature length in bytes */ |
| }; |
| |
| /* |
| * Credential context, slurm_cred_ctx_t: |
| */ |
| struct slurm_cred_context { |
| #ifndef NDEBUG |
| # define CRED_CTX_MAGIC 0x0c0c0c |
| int magic; |
| #endif |
| #if WITH_PTHREADS |
| pthread_mutex_t mutex; |
| #endif |
| enum ctx_type type; /* type of context (creator or verifier) */ |
| void *key; /* private or public key */ |
| List job_list; /* List of used jobids (for verifier) */ |
| List state_list; /* List of cred states (for verifier) */ |
| |
| int expiry_window;/* expiration window for cached creds */ |
| |
| void *exkey; /* Old public key if key is updated */ |
| time_t exkey_exp; /* Old key expiration time */ |
| }; |
| |
| |
| /* |
| * Completion of slurm job credential type, slurm_cred_t: |
| */ |
| struct slurm_job_credential { |
| #ifndef NDEBUG |
| # define CRED_MAGIC 0x0b0b0b |
| int magic; |
| #endif |
| #ifdef WITH_PTHREADS |
| pthread_mutex_t mutex; |
| #endif |
| uint32_t jobid; /* Job ID associated with this cred */ |
| uint32_t stepid; /* Job step ID for this credential */ |
| uid_t uid; /* user for which this cred is valid */ |
| |
| uint32_t job_mem_limit;/* MB of memory reserved per node OR |
| * real memory per CPU | MEM_PER_CPU, |
| * default=0 (no limit) */ |
| uint32_t step_mem_limit; |
| |
| uint16_t core_array_size; /* core/socket array size */ |
| uint16_t *cores_per_socket; |
| uint16_t *sockets_per_node; |
| uint32_t *sock_core_rep_count; |
| |
| List job_gres_list; /* Generic resources allocated to JOB */ |
| List step_gres_list; /* Generic resources allocated to STEP */ |
| |
| bitstr_t *job_core_bitmap; |
| uint16_t job_core_spec; /* Count of specialized cores */ |
| uint32_t job_nhosts; /* count of nodes allocated to JOB */ |
| char *job_hostlist; /* list of nodes allocated to JOB */ |
| bitstr_t *step_core_bitmap; |
| time_t ctime; /* time of credential creation */ |
| char *step_hostlist;/* hostnames for which the cred is ok */ |
| |
| char *signature; /* credential signature */ |
| unsigned int siglen; /* signature length in bytes */ |
| }; |
| |
| /* |
| * WARNING: Do not change the order of these fields or add additional |
| * fields at the beginning of the structure. If you do, job accounting |
| * plugins will stop working. If you need to add fields, add them |
| * at the end of the structure. |
| */ |
| typedef struct slurm_crypto_ops { |
| void *(*crypto_read_private_key) (const char *path); |
| void *(*crypto_read_public_key) (const char *path); |
| void (*crypto_destroy_key) (void *key); |
| int (*crypto_sign) (void * key, char *buffer, |
| int buf_size, char **sig_pp, |
| unsigned int *sig_size_p); |
| int (*crypto_verify_sign) (void * key, char *buffer, |
| unsigned int buf_size, |
| char *signature, |
| unsigned int sig_size); |
| const char *(*crypto_str_error) (int); |
| } slurm_crypto_ops_t; |
| |
| /* |
| * These strings must be in the same order as the fields declared |
| * for slurm_crypto_ops_t. |
| */ |
| static const char *syms[] = { |
| "crypto_read_private_key", |
| "crypto_read_public_key", |
| "crypto_destroy_key", |
| "crypto_sign", |
| "crypto_verify_sign", |
| "crypto_str_error" |
| }; |
| |
| struct sbcast_cache { |
| time_t expire; /* Time that the cred was created */ |
| uint32_t value; /* SLURM job id for this credential */ |
| }; |
| |
| static slurm_crypto_ops_t ops; |
| static plugin_context_t *g_context = NULL; |
| static pthread_mutex_t g_context_lock = PTHREAD_MUTEX_INITIALIZER; |
| static bool init_run = false; |
| static time_t crypto_restart_time = (time_t) 0; |
| static List sbcast_cache_list = NULL; |
| |
| /* |
| * Static prototypes: |
| */ |
| |
| static slurm_cred_ctx_t _slurm_cred_ctx_alloc(void); |
| static slurm_cred_t * _slurm_cred_alloc(void); |
| |
| static int _ctx_update_private_key(slurm_cred_ctx_t ctx, const char *path); |
| static int _ctx_update_public_key(slurm_cred_ctx_t ctx, const char *path); |
| static bool _exkey_is_valid(slurm_cred_ctx_t ctx); |
| |
| static cred_state_t * _cred_state_create(slurm_cred_ctx_t ctx, slurm_cred_t *c); |
| static job_state_t * _job_state_create(uint32_t jobid); |
| static void _cred_state_destroy(cred_state_t *cs); |
| static void _job_state_destroy(job_state_t *js); |
| |
| static job_state_t * _find_job_state(slurm_cred_ctx_t ctx, uint32_t jobid); |
| static job_state_t * _insert_job_state(slurm_cred_ctx_t ctx, uint32_t jobid); |
| static int _find_cred_state(cred_state_t *c, slurm_cred_t *cred); |
| |
| static void _insert_cred_state(slurm_cred_ctx_t ctx, slurm_cred_t *cred); |
| static void _clear_expired_job_states(slurm_cred_ctx_t ctx); |
| static void _clear_expired_credential_states(slurm_cred_ctx_t ctx); |
| static void _verifier_ctx_init(slurm_cred_ctx_t ctx); |
| |
| static bool _credential_replayed(slurm_cred_ctx_t ctx, slurm_cred_t *cred); |
| static bool _credential_revoked(slurm_cred_ctx_t ctx, slurm_cred_t *cred); |
| |
| static int _slurm_cred_sign(slurm_cred_ctx_t ctx, slurm_cred_t *cred, |
| uint16_t protocol_version); |
| static int _slurm_cred_verify_signature(slurm_cred_ctx_t ctx, slurm_cred_t *c, |
| uint16_t protocol_version); |
| |
| static int _slurm_crypto_init(void); |
| static int _slurm_crypto_fini(void); |
| |
| static job_state_t * _job_state_unpack_one(Buf buffer); |
| static cred_state_t * _cred_state_unpack_one(Buf buffer); |
| |
| static void _pack_cred(slurm_cred_t *cred, Buf buffer, |
| uint16_t protocol_version); |
| static void _job_state_unpack(slurm_cred_ctx_t ctx, Buf buffer); |
| static void _job_state_pack(slurm_cred_ctx_t ctx, Buf buffer); |
| static void _cred_state_unpack(slurm_cred_ctx_t ctx, Buf buffer); |
| static void _cred_state_pack(slurm_cred_ctx_t ctx, Buf buffer); |
| static void _job_state_pack_one(job_state_t *j, Buf buffer); |
| static void _cred_state_pack_one(cred_state_t *s, Buf buffer); |
| |
| static void _sbast_cache_add(sbcast_cred_t *sbcast_cred); |
| static void _sbcast_cache_del(void *x); |
| |
| #ifndef DISABLE_LOCALTIME |
| static char * timestr (const time_t *tp, char *buf, size_t n); |
| #endif |
| |
| static int _slurm_crypto_init(void) |
| { |
| char *plugin_type = "crypto"; |
| char *type = NULL; |
| int retval = SLURM_SUCCESS; |
| |
| if ( init_run && g_context ) /* mostly avoid locks for better speed */ |
| return retval; |
| |
| slurm_mutex_lock( &g_context_lock ); |
| if (crypto_restart_time == (time_t) 0) |
| crypto_restart_time = time(NULL); |
| if ( g_context ) |
| goto done; |
| |
| type = slurm_get_crypto_type(); |
| g_context = plugin_context_create( |
| plugin_type, type, (void **)&ops, syms, sizeof(syms)); |
| |
| if (!g_context) { |
| error("cannot create %s context for %s", plugin_type, type); |
| retval = SLURM_ERROR; |
| goto done; |
| } |
| sbcast_cache_list = list_create(_sbcast_cache_del); |
| init_run = true; |
| |
| done: |
| slurm_mutex_unlock( &g_context_lock ); |
| xfree(type); |
| |
| return(retval); |
| } |
| |
| static int _slurm_crypto_fini(void) |
| { |
| int rc; |
| |
| if (!g_context) |
| return SLURM_SUCCESS; |
| |
| init_run = false; |
| list_destroy(sbcast_cache_list); |
| sbcast_cache_list = NULL; |
| rc = plugin_context_destroy(g_context); |
| g_context = NULL; |
| return rc; |
| } |
| |
| /* Terminate the plugin and release all memory. */ |
| extern int slurm_crypto_fini(void) |
| { |
| if (_slurm_crypto_fini() < 0) |
| return SLURM_ERROR; |
| |
| return SLURM_SUCCESS; |
| } |
| |
| slurm_cred_ctx_t |
| slurm_cred_creator_ctx_create(const char *path) |
| { |
| slurm_cred_ctx_t ctx = NULL; |
| |
| if (_slurm_crypto_init() < 0) |
| return NULL; |
| |
| ctx = _slurm_cred_ctx_alloc(); |
| slurm_mutex_lock(&ctx->mutex); |
| |
| ctx->type = SLURM_CRED_CREATOR; |
| |
| ctx->key = (*(ops.crypto_read_private_key))(path); |
| if (!ctx->key) |
| goto fail; |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| return ctx; |
| |
| fail: |
| slurm_mutex_unlock(&ctx->mutex); |
| slurm_cred_ctx_destroy(ctx); |
| error("Can not open data encryption key file %s", path); |
| return NULL; |
| } |
| |
| |
| slurm_cred_ctx_t |
| slurm_cred_verifier_ctx_create(const char *path) |
| { |
| slurm_cred_ctx_t ctx = NULL; |
| |
| if (_slurm_crypto_init() < 0) |
| return NULL; |
| |
| ctx = _slurm_cred_ctx_alloc(); |
| slurm_mutex_lock(&ctx->mutex); |
| |
| ctx->type = SLURM_CRED_VERIFIER; |
| |
| ctx->key = (*(ops.crypto_read_public_key))(path); |
| if (!ctx->key) |
| goto fail; |
| |
| _verifier_ctx_init(ctx); |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| return ctx; |
| |
| fail: |
| slurm_mutex_unlock(&ctx->mutex); |
| slurm_cred_ctx_destroy(ctx); |
| error("Can not open data encryption key file %s", path); |
| return NULL; |
| } |
| |
| |
| void |
| slurm_cred_ctx_destroy(slurm_cred_ctx_t ctx) |
| { |
| if (ctx == NULL) |
| return; |
| if (_slurm_crypto_init() < 0) |
| return; |
| |
| slurm_mutex_lock(&ctx->mutex); |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| |
| if (ctx->exkey) |
| (*(ops.crypto_destroy_key))(ctx->exkey); |
| if (ctx->key) |
| (*(ops.crypto_destroy_key))(ctx->key); |
| if (ctx->job_list) |
| list_destroy(ctx->job_list); |
| if (ctx->state_list) |
| list_destroy(ctx->state_list); |
| |
| xassert(ctx->magic = ~CRED_CTX_MAGIC); |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| slurm_mutex_destroy(&ctx->mutex); |
| |
| xfree(ctx); |
| |
| return; |
| } |
| |
| int |
| slurm_cred_ctx_set(slurm_cred_ctx_t ctx, slurm_cred_opt_t opt, ...) |
| { |
| int rc = SLURM_SUCCESS; |
| va_list ap; |
| |
| xassert(ctx != NULL); |
| |
| va_start(ap, opt); |
| |
| slurm_mutex_lock(&ctx->mutex); |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| |
| switch (opt) { |
| case SLURM_CRED_OPT_EXPIRY_WINDOW: |
| ctx->expiry_window = va_arg(ap, int); |
| break; |
| default: |
| slurm_seterrno(EINVAL); |
| rc = SLURM_ERROR; |
| break; |
| } |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| |
| va_end(ap); |
| |
| return rc; |
| } |
| |
| int |
| slurm_cred_ctx_get(slurm_cred_ctx_t ctx, slurm_cred_opt_t opt, ...) |
| { |
| int rc = SLURM_SUCCESS; |
| va_list ap; |
| int *intp; |
| |
| xassert(ctx != NULL); |
| |
| va_start(ap, opt); |
| |
| slurm_mutex_lock(&ctx->mutex); |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| |
| switch (opt) { |
| case SLURM_CRED_OPT_EXPIRY_WINDOW: |
| intp = va_arg(ap, int *); |
| *intp = ctx->expiry_window; |
| break; |
| default: |
| slurm_seterrno(EINVAL); |
| rc = SLURM_ERROR; |
| break; |
| } |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| |
| va_end(ap); |
| |
| return rc; |
| } |
| |
| int |
| slurm_cred_ctx_key_update(slurm_cred_ctx_t ctx, const char *path) |
| { |
| if (_slurm_crypto_init() < 0) |
| return SLURM_ERROR; |
| |
| if (ctx->type == SLURM_CRED_CREATOR) |
| return _ctx_update_private_key(ctx, path); |
| else |
| return _ctx_update_public_key(ctx, path); |
| } |
| |
| |
| slurm_cred_t * |
| slurm_cred_create(slurm_cred_ctx_t ctx, slurm_cred_arg_t *arg, |
| uint16_t protocol_version) |
| { |
| slurm_cred_t *cred = NULL; |
| |
| xassert(ctx != NULL); |
| xassert(arg != NULL); |
| if (_slurm_crypto_init() < 0) |
| return NULL; |
| |
| cred = _slurm_cred_alloc(); |
| slurm_mutex_lock(&cred->mutex); |
| xassert(cred->magic == CRED_MAGIC); |
| |
| cred->jobid = arg->jobid; |
| cred->stepid = arg->stepid; |
| cred->uid = arg->uid; |
| cred->job_core_spec = arg->job_core_spec; |
| cred->job_gres_list = gres_plugin_job_state_dup(arg->job_gres_list); |
| cred->step_gres_list = gres_plugin_step_state_dup(arg->step_gres_list); |
| cred->job_mem_limit = arg->job_mem_limit; |
| cred->step_mem_limit = arg->step_mem_limit; |
| cred->step_hostlist = xstrdup(arg->step_hostlist); |
| #ifndef HAVE_BG |
| { |
| int i, sock_recs = 0; |
| #ifndef HAVE_ALPS_CRAY |
| /* Zero compute node allocations allowed on a Cray for use |
| * of front-end nodes */ |
| xassert(arg->job_nhosts); |
| #endif |
| for (i = 0; i < arg->job_nhosts; i++) { |
| sock_recs += arg->sock_core_rep_count[i]; |
| if (sock_recs >= arg->job_nhosts) |
| break; |
| } |
| i++; |
| cred->job_core_bitmap = bit_copy(arg->job_core_bitmap); |
| cred->step_core_bitmap = bit_copy(arg->step_core_bitmap); |
| cred->core_array_size = i; |
| cred->cores_per_socket = xmalloc(sizeof(uint16_t) * i); |
| memcpy(cred->cores_per_socket, arg->cores_per_socket, |
| (sizeof(uint16_t) * i)); |
| cred->sockets_per_node = xmalloc(sizeof(uint16_t) * i); |
| memcpy(cred->sockets_per_node, arg->sockets_per_node, |
| (sizeof(uint16_t) * i)); |
| cred->sock_core_rep_count = xmalloc(sizeof(uint32_t) * i); |
| memcpy(cred->sock_core_rep_count, arg->sock_core_rep_count, |
| (sizeof(uint32_t) * i)); |
| cred->job_nhosts = arg->job_nhosts; |
| cred->job_hostlist = xstrdup(arg->job_hostlist); |
| } |
| #endif |
| cred->ctime = time(NULL); |
| |
| slurm_mutex_lock(&ctx->mutex); |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_CREATOR); |
| if (_slurm_cred_sign(ctx, cred, protocol_version) < 0) |
| goto fail; |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| slurm_mutex_unlock(&cred->mutex); |
| |
| return cred; |
| |
| fail: |
| slurm_mutex_unlock(&ctx->mutex); |
| slurm_mutex_unlock(&cred->mutex); |
| slurm_cred_destroy(cred); |
| return NULL; |
| } |
| |
| slurm_cred_t * |
| slurm_cred_copy(slurm_cred_t *cred) |
| { |
| slurm_cred_t *rcred = NULL; |
| |
| xassert(cred != NULL); |
| |
| slurm_mutex_lock(&cred->mutex); |
| |
| rcred = _slurm_cred_alloc(); |
| slurm_mutex_lock(&rcred->mutex); |
| xassert(rcred->magic == CRED_MAGIC); |
| |
| rcred->jobid = cred->jobid; |
| rcred->stepid = cred->stepid; |
| rcred->uid = cred->uid; |
| rcred->job_core_spec = cred->job_core_spec; |
| rcred->job_gres_list = gres_plugin_job_state_dup(cred->job_gres_list); |
| rcred->step_gres_list = gres_plugin_step_state_dup(cred->step_gres_list); |
| rcred->job_mem_limit = cred->job_mem_limit; |
| rcred->step_mem_limit = cred->step_mem_limit; |
| rcred->step_hostlist = xstrdup(cred->step_hostlist); |
| #ifndef HAVE_BG |
| rcred->job_core_bitmap = bit_copy(cred->job_core_bitmap); |
| rcred->step_core_bitmap = bit_copy(cred->step_core_bitmap); |
| rcred->core_array_size = cred->core_array_size; |
| rcred->cores_per_socket = xmalloc(sizeof(uint16_t) * |
| rcred->core_array_size); |
| memcpy(rcred->cores_per_socket, cred->cores_per_socket, |
| (sizeof(uint16_t) * rcred->core_array_size)); |
| rcred->sockets_per_node = xmalloc(sizeof(uint16_t) * |
| rcred->core_array_size); |
| memcpy(rcred->sockets_per_node, cred->sockets_per_node, |
| (sizeof(uint16_t) * rcred->core_array_size)); |
| cred->sock_core_rep_count = xmalloc(sizeof(uint32_t) * |
| rcred->core_array_size); |
| memcpy(rcred->sock_core_rep_count, cred->sock_core_rep_count, |
| (sizeof(uint32_t) * rcred->core_array_size)); |
| rcred->job_nhosts = cred->job_nhosts; |
| rcred->job_hostlist = xstrdup(cred->job_hostlist); |
| #endif |
| rcred->ctime = cred->ctime; |
| rcred->siglen = cred->siglen; |
| /* Assumes signature is a string, |
| * otherwise use xmalloc and strcpy here */ |
| rcred->signature = xstrdup(cred->signature); |
| |
| slurm_mutex_unlock(&cred->mutex); |
| slurm_mutex_unlock(&rcred->mutex); |
| |
| return rcred; |
| } |
| |
| slurm_cred_t * |
| slurm_cred_faker(slurm_cred_arg_t *arg) |
| { |
| int fd, i; |
| slurm_cred_t *cred = NULL; |
| |
| xassert(arg != NULL); |
| |
| cred = _slurm_cred_alloc(); |
| slurm_mutex_lock(&cred->mutex); |
| |
| cred->jobid = arg->jobid; |
| cred->stepid = arg->stepid; |
| cred->uid = arg->uid; |
| cred->job_core_spec = arg->job_core_spec; |
| cred->job_mem_limit = arg->job_mem_limit; |
| cred->step_mem_limit = arg->step_mem_limit; |
| cred->step_hostlist = xstrdup(arg->step_hostlist); |
| #ifndef HAVE_BG |
| { |
| int sock_recs = 0; |
| for (i=0; i<arg->job_nhosts; i++) { |
| sock_recs += arg->sock_core_rep_count[i]; |
| if (sock_recs >= arg->job_nhosts) |
| break; |
| } |
| i++; |
| cred->job_core_bitmap = bit_copy(arg->job_core_bitmap); |
| cred->step_core_bitmap = bit_copy(arg->step_core_bitmap); |
| cred->core_array_size = i; |
| cred->cores_per_socket = xmalloc(sizeof(uint16_t) * i); |
| memcpy(cred->cores_per_socket, arg->cores_per_socket, |
| (sizeof(uint16_t) * i)); |
| cred->sockets_per_node = xmalloc(sizeof(uint16_t) * i); |
| memcpy(cred->sockets_per_node, arg->sockets_per_node, |
| (sizeof(uint16_t) * i)); |
| cred->sock_core_rep_count = xmalloc(sizeof(uint32_t) * i); |
| memcpy(cred->sock_core_rep_count, arg->sock_core_rep_count, |
| (sizeof(uint32_t) * i)); |
| cred->job_nhosts = arg->job_nhosts; |
| cred->job_hostlist = xstrdup(arg->job_hostlist); |
| } |
| #endif |
| cred->ctime = time(NULL); |
| cred->siglen = SLURM_IO_KEY_SIZE; |
| |
| cred->signature = xmalloc(cred->siglen * sizeof(char)); |
| |
| if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) { |
| if (read(fd, cred->signature, cred->siglen-1) == -1) |
| error("reading fake signature from /dev/urandom: %m"); |
| if (close(fd) < 0) |
| error("close(/dev/urandom): %m"); |
| for (i=0; i<cred->siglen-1; i++) |
| cred->signature[i] = 'a' + (cred->signature[i] & 0xf); |
| } else { /* Note: some systems lack this file */ |
| struct timeval tv; |
| gettimeofday(&tv, NULL); |
| i = (unsigned int) (tv.tv_sec + tv.tv_usec); |
| srand((unsigned int) i); |
| for (i=0; i<cred->siglen-1; i++) |
| cred->signature[i] = 'a' + (rand() & 0xf); |
| } |
| |
| slurm_mutex_unlock(&cred->mutex); |
| return cred; |
| |
| } |
| |
| void slurm_cred_free_args(slurm_cred_arg_t *arg) |
| { |
| FREE_NULL_BITMAP(arg->job_core_bitmap); |
| FREE_NULL_BITMAP(arg->step_core_bitmap); |
| xfree(arg->cores_per_socket); |
| FREE_NULL_LIST(arg->job_gres_list); |
| FREE_NULL_LIST(arg->step_gres_list); |
| xfree(arg->step_hostlist); |
| xfree(arg->job_hostlist); |
| xfree(arg->sock_core_rep_count); |
| xfree(arg->sockets_per_node); |
| } |
| |
| int slurm_cred_get_args(slurm_cred_t *cred, slurm_cred_arg_t *arg) |
| { |
| xassert(cred != NULL); |
| xassert(arg != NULL); |
| |
| /* |
| * set arguments to cred contents |
| */ |
| slurm_mutex_lock(&cred->mutex); |
| arg->jobid = cred->jobid; |
| arg->stepid = cred->stepid; |
| arg->uid = cred->uid; |
| arg->job_gres_list = gres_plugin_job_state_dup(cred->job_gres_list); |
| arg->step_gres_list = gres_plugin_step_state_dup(cred->step_gres_list); |
| arg->job_core_spec = cred->job_core_spec; |
| arg->job_mem_limit = cred->job_mem_limit; |
| arg->step_mem_limit = cred->step_mem_limit; |
| arg->step_hostlist = xstrdup(cred->step_hostlist); |
| #ifdef HAVE_BG |
| arg->job_core_bitmap = NULL; |
| arg->step_core_bitmap = NULL; |
| arg->cores_per_socket = NULL; |
| arg->sockets_per_node = NULL; |
| arg->sock_core_rep_count = NULL; |
| arg->job_nhosts = 0; |
| arg->job_hostlist = NULL; |
| #else |
| arg->job_core_bitmap = bit_copy(cred->job_core_bitmap); |
| arg->step_core_bitmap = bit_copy(cred->step_core_bitmap); |
| arg->cores_per_socket = xmalloc(sizeof(uint16_t) * |
| cred->core_array_size); |
| memcpy(arg->cores_per_socket, cred->cores_per_socket, |
| (sizeof(uint16_t) * cred->core_array_size)); |
| arg->sockets_per_node = xmalloc(sizeof(uint16_t) * |
| cred->core_array_size); |
| memcpy(arg->sockets_per_node, cred->sockets_per_node, |
| (sizeof(uint16_t) * cred->core_array_size)); |
| arg->sock_core_rep_count = xmalloc(sizeof(uint32_t) * |
| cred->core_array_size); |
| memcpy(arg->sock_core_rep_count, cred->sock_core_rep_count, |
| (sizeof(uint32_t) * cred->core_array_size)); |
| arg->job_nhosts = cred->job_nhosts; |
| arg->job_hostlist = xstrdup(cred->job_hostlist); |
| #endif |
| slurm_mutex_unlock(&cred->mutex); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| extern int |
| slurm_cred_verify(slurm_cred_ctx_t ctx, slurm_cred_t *cred, |
| slurm_cred_arg_t *arg, uint16_t protocol_version) |
| { |
| time_t now = time(NULL); |
| int errnum; |
| |
| xassert(ctx != NULL); |
| xassert(cred != NULL); |
| xassert(arg != NULL); |
| if (_slurm_crypto_init() < 0) |
| return SLURM_ERROR; |
| |
| slurm_mutex_lock(&ctx->mutex); |
| slurm_mutex_lock(&cred->mutex); |
| |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_VERIFIER); |
| xassert(cred->magic == CRED_MAGIC); |
| |
| /* NOTE: the verification checks that the credential was |
| * created by SlurmUser or root */ |
| if (_slurm_cred_verify_signature(ctx, cred, protocol_version) < 0) { |
| slurm_seterrno(ESLURMD_INVALID_JOB_CREDENTIAL); |
| goto error; |
| } |
| |
| if (now > (cred->ctime + ctx->expiry_window)) { |
| slurm_seterrno(ESLURMD_CREDENTIAL_EXPIRED); |
| goto error; |
| } |
| |
| slurm_cred_handle_reissue(ctx, cred); |
| |
| if (_credential_revoked(ctx, cred)) { |
| slurm_seterrno(ESLURMD_CREDENTIAL_REVOKED); |
| goto error; |
| } |
| |
| if (_credential_replayed(ctx, cred)) { |
| slurm_seterrno(ESLURMD_CREDENTIAL_REPLAYED); |
| goto error; |
| } |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| |
| /* |
| * set arguments to cred contents |
| */ |
| arg->jobid = cred->jobid; |
| arg->stepid = cred->stepid; |
| arg->uid = cred->uid; |
| arg->job_gres_list = gres_plugin_job_state_dup(cred->job_gres_list); |
| arg->step_gres_list = gres_plugin_step_state_dup(cred->step_gres_list); |
| arg->job_core_spec = cred->job_core_spec; |
| arg->job_mem_limit = cred->job_mem_limit; |
| arg->step_mem_limit = cred->step_mem_limit; |
| arg->step_hostlist = xstrdup(cred->step_hostlist); |
| |
| #ifdef HAVE_BG |
| arg->job_core_bitmap = NULL; |
| arg->step_core_bitmap = NULL; |
| arg->cores_per_socket = NULL; |
| arg->sockets_per_node = NULL; |
| arg->sock_core_rep_count = NULL; |
| arg->job_nhosts = 0; |
| arg->job_hostlist = NULL; |
| #else |
| arg->job_core_bitmap = bit_copy(cred->job_core_bitmap); |
| arg->step_core_bitmap = bit_copy(cred->step_core_bitmap); |
| arg->cores_per_socket = xmalloc(sizeof(uint16_t) * |
| cred->core_array_size); |
| memcpy(arg->cores_per_socket, cred->cores_per_socket, |
| (sizeof(uint16_t) * cred->core_array_size)); |
| arg->sockets_per_node = xmalloc(sizeof(uint16_t) * |
| cred->core_array_size); |
| memcpy(arg->sockets_per_node, cred->sockets_per_node, |
| (sizeof(uint16_t) * cred->core_array_size)); |
| arg->sock_core_rep_count = xmalloc(sizeof(uint32_t) * |
| cred->core_array_size); |
| memcpy(arg->sock_core_rep_count, cred->sock_core_rep_count, |
| (sizeof(uint32_t) * cred->core_array_size)); |
| arg->job_nhosts = cred->job_nhosts; |
| arg->job_hostlist = xstrdup(cred->job_hostlist); |
| #endif |
| slurm_mutex_unlock(&cred->mutex); |
| |
| return SLURM_SUCCESS; |
| |
| error: |
| errnum = slurm_get_errno(); |
| slurm_mutex_unlock(&ctx->mutex); |
| slurm_mutex_unlock(&cred->mutex); |
| slurm_seterrno(errnum); |
| return SLURM_ERROR; |
| } |
| |
| |
| void |
| slurm_cred_destroy(slurm_cred_t *cred) |
| { |
| if (cred == NULL) |
| return; |
| |
| xassert(cred->magic == CRED_MAGIC); |
| |
| slurm_mutex_lock(&cred->mutex); |
| #ifndef HAVE_BG |
| FREE_NULL_BITMAP(cred->job_core_bitmap); |
| FREE_NULL_BITMAP(cred->step_core_bitmap); |
| xfree(cred->cores_per_socket); |
| xfree(cred->job_hostlist); |
| xfree(cred->sock_core_rep_count); |
| xfree(cred->sockets_per_node); |
| #endif |
| FREE_NULL_LIST(cred->job_gres_list); |
| FREE_NULL_LIST(cred->step_gres_list); |
| xfree(cred->step_hostlist); |
| xfree(cred->signature); |
| xassert(cred->magic = ~CRED_MAGIC); |
| |
| slurm_mutex_unlock(&cred->mutex); |
| slurm_mutex_destroy(&cred->mutex); |
| |
| xfree(cred); |
| } |
| |
| |
| bool |
| slurm_cred_jobid_cached(slurm_cred_ctx_t ctx, uint32_t jobid) |
| { |
| bool retval = false; |
| |
| xassert(ctx != NULL); |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_VERIFIER); |
| |
| slurm_mutex_lock(&ctx->mutex); |
| |
| _clear_expired_job_states(ctx); |
| |
| /* |
| * Return true if we find a cached job state for job id `jobid' |
| */ |
| retval = (_find_job_state(ctx, jobid) != NULL); |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| |
| return retval; |
| } |
| |
| int |
| slurm_cred_insert_jobid(slurm_cred_ctx_t ctx, uint32_t jobid) |
| { |
| xassert(ctx != NULL); |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_VERIFIER); |
| |
| slurm_mutex_lock(&ctx->mutex); |
| |
| _clear_expired_job_states(ctx); |
| (void) _insert_job_state(ctx, jobid); |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| int |
| slurm_cred_rewind(slurm_cred_ctx_t ctx, slurm_cred_t *cred) |
| { |
| int rc = 0; |
| |
| xassert(ctx != NULL); |
| |
| slurm_mutex_lock(&ctx->mutex); |
| |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_VERIFIER); |
| |
| rc = list_delete_all(ctx->state_list, |
| (ListFindF) _find_cred_state, cred); |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| |
| return (rc > 0 ? SLURM_SUCCESS : SLURM_FAILURE); |
| } |
| |
| int |
| slurm_cred_revoke(slurm_cred_ctx_t ctx, uint32_t jobid, time_t time, |
| time_t start_time) |
| { |
| job_state_t *j = NULL; |
| |
| xassert(ctx != NULL); |
| |
| slurm_mutex_lock(&ctx->mutex); |
| |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_VERIFIER); |
| |
| _clear_expired_job_states(ctx); |
| |
| if (!(j = _find_job_state(ctx, jobid))) { |
| /* |
| * This node has not yet seen a job step for this |
| * job. Insert a job state object so that we can |
| * revoke any future credentials. |
| */ |
| j = _insert_job_state(ctx, jobid); |
| } |
| if (j->revoked) { |
| if (start_time && (j->revoked < start_time)) { |
| debug("job %u requeued, but started no tasks", jobid); |
| j->expiration = (time_t) MAX_TIME; |
| } else { |
| slurm_seterrno(EEXIST); |
| goto error; |
| } |
| } |
| |
| j->revoked = time; |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| return SLURM_SUCCESS; |
| |
| error: |
| slurm_mutex_unlock(&ctx->mutex); |
| return SLURM_FAILURE; |
| } |
| |
| int |
| slurm_cred_begin_expiration(slurm_cred_ctx_t ctx, uint32_t jobid) |
| { |
| job_state_t *j = NULL; |
| |
| xassert(ctx != NULL); |
| |
| slurm_mutex_lock(&ctx->mutex); |
| |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_VERIFIER); |
| |
| _clear_expired_job_states(ctx); |
| |
| if (!(j = _find_job_state(ctx, jobid))) { |
| slurm_seterrno(ESRCH); |
| goto error; |
| } |
| |
| if (j->expiration < (time_t) MAX_TIME) { |
| slurm_seterrno(EEXIST); |
| goto error; |
| } |
| |
| j->expiration = time(NULL) + ctx->expiry_window; |
| #if DEBUG_TIME |
| { |
| char buf[64]; |
| debug2("set revoke expiration for jobid %u to %s", |
| j->jobid, timestr(&j->expiration, buf, 64)); |
| } |
| #else |
| debug2("set revoke expiration for jobid %u to %"PRIu64" UTS", |
| j->jobid, (uint64_t) j->expiration); |
| #endif |
| slurm_mutex_unlock(&ctx->mutex); |
| return SLURM_SUCCESS; |
| |
| error: |
| slurm_mutex_unlock(&ctx->mutex); |
| return SLURM_ERROR; |
| } |
| |
| int |
| slurm_cred_get_signature(slurm_cred_t *cred, char **datap, uint32_t *datalen) |
| { |
| xassert(cred != NULL); |
| xassert(datap != NULL); |
| xassert(datalen != NULL); |
| |
| slurm_mutex_lock(&cred->mutex); |
| |
| *datap = (char *) cred->signature; |
| *datalen = cred->siglen; |
| |
| slurm_mutex_unlock(&cred->mutex); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| #ifndef HAVE_BG |
| /* Convert bitmap to string representation with brackets removed */ |
| static char *_core_format(bitstr_t *core_bitmap) |
| { |
| char str[1024], *bracket_ptr; |
| |
| bit_fmt(str, sizeof(str), core_bitmap); |
| if (str[0] != '[') |
| return xstrdup(str); |
| |
| /* strip off brackets */ |
| bracket_ptr = strchr(str, ']'); |
| if (bracket_ptr) |
| bracket_ptr[0] = '\0'; |
| return xstrdup(str+1); |
| } |
| #endif |
| |
| /* |
| * Retrieve the set of cores that were allocated to the job and step then |
| * format them in the List Format (e.g., "0-2,7,12-14"). Also return |
| * job and step's memory limit. |
| * |
| * NOTE: caller must xfree the returned strings. |
| */ |
| void format_core_allocs(slurm_cred_t *cred, char *node_name, uint16_t cpus, |
| char **job_alloc_cores, char **step_alloc_cores, |
| uint32_t *job_mem_limit, uint32_t *step_mem_limit) |
| { |
| #ifdef HAVE_BG |
| xassert(cred); |
| xassert(job_alloc_cores); |
| xassert(step_alloc_cores); |
| *job_alloc_cores = NULL; |
| *step_alloc_cores = NULL; |
| *job_mem_limit = cred->job_mem_limit & (~MEM_PER_CPU); |
| if (cred->step_mem_limit) |
| *step_mem_limit = cred->step_mem_limit & (~MEM_PER_CPU); |
| else |
| *step_mem_limit = *job_mem_limit; |
| #else |
| bitstr_t *job_core_bitmap, *step_core_bitmap; |
| hostset_t hset = NULL; |
| int host_index = -1; |
| uint32_t i, j, i_first_bit=0, i_last_bit=0; |
| uint32_t job_cpu_cnt = 0, step_cpu_cnt = 0; |
| |
| xassert(cred); |
| xassert(job_alloc_cores); |
| xassert(step_alloc_cores); |
| if (!(hset = hostset_create(cred->job_hostlist))) { |
| error("Unable to create job hostset: `%s'", |
| cred->job_hostlist); |
| return; |
| } |
| #ifdef HAVE_FRONT_END |
| host_index = 0; |
| #else |
| host_index = hostset_find(hset, node_name); |
| #endif |
| if ((host_index < 0) || (host_index >= cred->job_nhosts)) { |
| error("Invalid host_index %d for job %u", |
| host_index, cred->jobid); |
| error("Host %s not in hostlist %s", |
| node_name, cred->job_hostlist); |
| hostset_destroy(hset); |
| return; |
| } |
| host_index++; /* change from 0-origin to 1-origin */ |
| for (i=0; host_index; i++) { |
| if (host_index > cred->sock_core_rep_count[i]) { |
| i_first_bit += cred->sockets_per_node[i] * |
| cred->cores_per_socket[i] * |
| cred->sock_core_rep_count[i]; |
| host_index -= cred->sock_core_rep_count[i]; |
| } else { |
| i_first_bit += cred->sockets_per_node[i] * |
| cred->cores_per_socket[i] * |
| (host_index - 1); |
| i_last_bit = i_first_bit + |
| cred->sockets_per_node[i] * |
| cred->cores_per_socket[i]; |
| break; |
| } |
| } |
| |
| job_core_bitmap = bit_alloc(i_last_bit - i_first_bit); |
| step_core_bitmap = bit_alloc(i_last_bit - i_first_bit); |
| for (i = i_first_bit, j = 0; i < i_last_bit; i++, j++) { |
| if (bit_test(cred->job_core_bitmap, i)) { |
| bit_set(job_core_bitmap, j); |
| job_cpu_cnt++; |
| } |
| if (bit_test(cred->step_core_bitmap, i)) { |
| bit_set(step_core_bitmap, j); |
| step_cpu_cnt++; |
| } |
| } |
| |
| /* Scale CPU count, same as slurmd/req.c:_check_job_credential() */ |
| if (i_last_bit <= i_first_bit) |
| error("step credential has no CPUs selected"); |
| else { |
| uint32_t i = cpus / (i_last_bit - i_first_bit); |
| if (i > 1) { |
| info("scaling CPU count by factor of %d (%u/(%u-%u)", |
| i, cpus, i_last_bit, i_first_bit); |
| step_cpu_cnt *= i; |
| job_cpu_cnt *= i; |
| } |
| } |
| |
| if (cred->job_mem_limit & MEM_PER_CPU) { |
| *job_mem_limit = (cred->job_mem_limit & (~MEM_PER_CPU)) * |
| job_cpu_cnt; |
| } else |
| *job_mem_limit = cred->job_mem_limit; |
| if (cred->step_mem_limit & MEM_PER_CPU) { |
| *step_mem_limit = (cred->step_mem_limit & (~MEM_PER_CPU)) * |
| step_cpu_cnt; |
| } else if (cred->step_mem_limit) |
| *step_mem_limit = cred->step_mem_limit; |
| else |
| *step_mem_limit = *job_mem_limit; |
| |
| *job_alloc_cores = _core_format(job_core_bitmap); |
| *step_alloc_cores = _core_format(step_core_bitmap); |
| FREE_NULL_BITMAP(job_core_bitmap); |
| FREE_NULL_BITMAP(step_core_bitmap); |
| hostset_destroy(hset); |
| #endif |
| } |
| |
| /* |
| * Retrieve the job and step generic resources (gres) allocate to this job |
| * on this node. |
| * |
| * NOTE: Caller must destroy the returned lists |
| */ |
| extern void get_cred_gres(slurm_cred_t *cred, char *node_name, |
| List *job_gres_list, List *step_gres_list) |
| { |
| hostset_t hset = NULL; |
| int host_index = -1; |
| |
| xassert(cred); |
| xassert(job_gres_list); |
| xassert(step_gres_list); |
| |
| *job_gres_list = NULL; |
| *step_gres_list = NULL; |
| if ((cred->job_gres_list == NULL) && (cred->step_gres_list == NULL)) |
| return; |
| |
| if (!(hset = hostset_create(cred->job_hostlist))) { |
| error("Unable to create job hostset: `%s'", |
| cred->job_hostlist); |
| return; |
| } |
| #ifdef HAVE_FRONT_END |
| host_index = 0; |
| #else |
| host_index = hostset_find(hset, node_name); |
| #endif |
| if ((host_index < 0) || (host_index >= cred->job_nhosts)) { |
| error("Invalid host_index %d for job %u", |
| host_index, cred->jobid); |
| error("Host %s not in credential hostlist %s", |
| node_name, cred->job_hostlist); |
| hostset_destroy(hset); |
| return; |
| } |
| |
| *job_gres_list = gres_plugin_job_state_extract(cred->job_gres_list, |
| host_index); |
| *step_gres_list = gres_plugin_step_state_extract(cred->step_gres_list, |
| host_index); |
| return; |
| } |
| |
| void |
| slurm_cred_pack(slurm_cred_t *cred, Buf buffer, uint16_t protocol_version) |
| { |
| xassert(cred != NULL); |
| xassert(cred->magic == CRED_MAGIC); |
| |
| slurm_mutex_lock(&cred->mutex); |
| |
| _pack_cred(cred, buffer, protocol_version); |
| xassert(cred->siglen > 0); |
| packmem(cred->signature, cred->siglen, buffer); |
| |
| slurm_mutex_unlock(&cred->mutex); |
| |
| return; |
| } |
| |
| slurm_cred_t * |
| slurm_cred_unpack(Buf buffer, uint16_t protocol_version) |
| { |
| uint32_t cred_uid, len; |
| slurm_cred_t *cred = NULL; |
| char *bit_fmt = NULL; |
| char **sigp; |
| uint32_t cluster_flags = slurmdb_setup_cluster_flags(); |
| |
| xassert(buffer != NULL); |
| |
| cred = _slurm_cred_alloc(); |
| slurm_mutex_lock(&cred->mutex); |
| if (protocol_version >= SLURM_14_03_PROTOCOL_VERSION) { |
| safe_unpack32(&cred->jobid, buffer); |
| safe_unpack32(&cred->stepid, buffer); |
| safe_unpack32(&cred_uid, buffer); |
| cred->uid = cred_uid; |
| if (gres_plugin_job_state_unpack(&cred->job_gres_list, buffer, |
| cred->jobid, protocol_version) |
| != SLURM_SUCCESS) |
| goto unpack_error; |
| if (gres_plugin_step_state_unpack(&cred->step_gres_list, |
| buffer, cred->jobid, |
| cred->stepid, |
| protocol_version) |
| != SLURM_SUCCESS) { |
| goto unpack_error; |
| } |
| safe_unpack16(&cred->job_core_spec, buffer); |
| safe_unpack32(&cred->job_mem_limit, buffer); |
| safe_unpack32(&cred->step_mem_limit, buffer); |
| safe_unpackstr_xmalloc(&cred->step_hostlist, &len, buffer); |
| safe_unpack_time(&cred->ctime, buffer); |
| |
| if (!(cluster_flags & CLUSTER_FLAG_BG)) { |
| uint32_t tot_core_cnt; |
| safe_unpack32(&tot_core_cnt, buffer); |
| safe_unpackstr_xmalloc(&bit_fmt, &len, buffer); |
| cred->job_core_bitmap = |
| bit_alloc((bitoff_t) tot_core_cnt); |
| if (bit_unfmt(cred->job_core_bitmap, bit_fmt)) |
| goto unpack_error; |
| xfree(bit_fmt); |
| safe_unpackstr_xmalloc(&bit_fmt, &len, buffer); |
| cred->step_core_bitmap = |
| bit_alloc((bitoff_t) tot_core_cnt); |
| if (bit_unfmt(cred->step_core_bitmap, bit_fmt)) |
| goto unpack_error; |
| xfree(bit_fmt); |
| safe_unpack16(&cred->core_array_size, buffer); |
| if (cred->core_array_size) { |
| safe_unpack16_array(&cred->cores_per_socket, |
| &len, |
| buffer); |
| if (len != cred->core_array_size) |
| goto unpack_error; |
| safe_unpack16_array(&cred->sockets_per_node, |
| &len, buffer); |
| if (len != cred->core_array_size) |
| goto unpack_error; |
| safe_unpack32_array(&cred->sock_core_rep_count, |
| &len, |
| buffer); |
| if (len != cred->core_array_size) |
| goto unpack_error; |
| } |
| safe_unpack32(&cred->job_nhosts, buffer); |
| safe_unpackstr_xmalloc(&cred->job_hostlist, &len, |
| buffer); |
| } |
| |
| /* "sigp" must be last */ |
| sigp = (char **) &cred->signature; |
| safe_unpackmem_xmalloc(sigp, &len, buffer); |
| cred->siglen = len; |
| xassert(len > 0); |
| } else if (protocol_version >= SLURM_2_5_PROTOCOL_VERSION) { |
| safe_unpack32(&cred->jobid, buffer); |
| safe_unpack32(&cred->stepid, buffer); |
| safe_unpack32(&cred_uid, buffer); |
| cred->uid = cred_uid; |
| if (gres_plugin_job_state_unpack(&cred->job_gres_list, buffer, |
| cred->jobid, protocol_version) |
| != SLURM_SUCCESS) |
| goto unpack_error; |
| if (gres_plugin_step_state_unpack(&cred->step_gres_list, |
| buffer, cred->jobid, |
| cred->stepid, |
| protocol_version) |
| != SLURM_SUCCESS) { |
| goto unpack_error; |
| } |
| safe_unpack32(&cred->job_mem_limit, buffer); |
| safe_unpack32(&cred->step_mem_limit, buffer); |
| safe_unpackstr_xmalloc(&cred->step_hostlist, &len, buffer); |
| safe_unpack_time(&cred->ctime, buffer); |
| |
| if (!(cluster_flags & CLUSTER_FLAG_BG)) { |
| uint32_t tot_core_cnt; |
| safe_unpack32(&tot_core_cnt, buffer); |
| safe_unpackstr_xmalloc(&bit_fmt, &len, buffer); |
| cred->job_core_bitmap = |
| bit_alloc((bitoff_t) tot_core_cnt); |
| if (bit_unfmt(cred->job_core_bitmap, bit_fmt)) |
| goto unpack_error; |
| xfree(bit_fmt); |
| safe_unpackstr_xmalloc(&bit_fmt, &len, buffer); |
| cred->step_core_bitmap = |
| bit_alloc((bitoff_t) tot_core_cnt); |
| if (bit_unfmt(cred->step_core_bitmap, bit_fmt)) |
| goto unpack_error; |
| xfree(bit_fmt); |
| safe_unpack16(&cred->core_array_size, buffer); |
| if (cred->core_array_size) { |
| safe_unpack16_array(&cred->cores_per_socket, |
| &len, |
| buffer); |
| if (len != cred->core_array_size) |
| goto unpack_error; |
| safe_unpack16_array(&cred->sockets_per_node, |
| &len, buffer); |
| if (len != cred->core_array_size) |
| goto unpack_error; |
| safe_unpack32_array(&cred->sock_core_rep_count, |
| &len, |
| buffer); |
| if (len != cred->core_array_size) |
| goto unpack_error; |
| } |
| safe_unpack32(&cred->job_nhosts, buffer); |
| safe_unpackstr_xmalloc(&cred->job_hostlist, &len, |
| buffer); |
| } |
| |
| /* "sigp" must be last */ |
| sigp = (char **) &cred->signature; |
| safe_unpackmem_xmalloc(sigp, &len, buffer); |
| cred->siglen = len; |
| xassert(len > 0); |
| } else { |
| error("slurm_cred_unpack: protocol_version" |
| " %hu not supported", protocol_version); |
| goto unpack_error; |
| } |
| slurm_mutex_unlock(&cred->mutex); |
| return cred; |
| |
| unpack_error: |
| xfree(bit_fmt); |
| slurm_mutex_unlock(&cred->mutex); |
| slurm_cred_destroy(cred); |
| return NULL; |
| } |
| |
| int |
| slurm_cred_ctx_pack(slurm_cred_ctx_t ctx, Buf buffer) |
| { |
| slurm_mutex_lock(&ctx->mutex); |
| _job_state_pack(ctx, buffer); |
| _cred_state_pack(ctx, buffer); |
| slurm_mutex_unlock(&ctx->mutex); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| int |
| slurm_cred_ctx_unpack(slurm_cred_ctx_t ctx, Buf buffer) |
| { |
| xassert(ctx != NULL); |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_VERIFIER); |
| |
| slurm_mutex_lock(&ctx->mutex); |
| |
| /* |
| * Unpack job state list and cred state list from buffer |
| * appening them onto ctx->state_list and ctx->job_list. |
| */ |
| _job_state_unpack(ctx, buffer); |
| _cred_state_unpack(ctx, buffer); |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| void |
| slurm_cred_print(slurm_cred_t *cred) |
| { |
| if (cred == NULL) |
| return; |
| |
| slurm_mutex_lock(&cred->mutex); |
| |
| xassert(cred->magic == CRED_MAGIC); |
| |
| info("Cred: Jobid %u", cred->jobid ); |
| info("Cred: Stepid %u", cred->stepid ); |
| info("Cred: UID %u", (uint32_t) cred->uid); |
| info("Cred: Job_core_spec %u", cred->job_core_spec ); |
| info("Cred: Job_mem_limit %u", cred->job_mem_limit ); |
| info("Cred: Step_mem_limit %u", cred->step_mem_limit ); |
| info("Cred: Step hostlist %s", cred->step_hostlist ); |
| info("Cred: ctime %s", slurm_ctime(&cred->ctime) ); |
| info("Cred: siglen %u", cred->siglen ); |
| #ifndef HAVE_BG |
| { |
| int i; |
| char str[128]; |
| info("Cred: job_core_bitmap %s", |
| bit_fmt(str, sizeof(str), cred->job_core_bitmap)); |
| info("Cred: step_core_bitmap %s", |
| bit_fmt(str, sizeof(str), cred->step_core_bitmap)); |
| info("Cred: sockets_per_node, cores_per_socket, rep_count"); |
| for (i=0; i<cred->core_array_size; i++) { |
| info(" socks:%u cores:%u reps:%u", |
| cred->sockets_per_node[i], |
| cred->cores_per_socket[i], |
| cred->sock_core_rep_count[i]); |
| } |
| info("Cred: job_nhosts %u", cred->job_nhosts ); |
| info("Cred: job_hostlist %s", cred->job_hostlist ); |
| } |
| #endif |
| slurm_mutex_unlock(&cred->mutex); |
| |
| } |
| |
| static void |
| _verifier_ctx_init(slurm_cred_ctx_t ctx) |
| { |
| xassert(ctx != NULL); |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_VERIFIER); |
| |
| ctx->job_list = list_create((ListDelF) _job_state_destroy); |
| ctx->state_list = list_create((ListDelF) _cred_state_destroy); |
| |
| return; |
| } |
| |
| |
| static int |
| _ctx_update_private_key(slurm_cred_ctx_t ctx, const char *path) |
| { |
| void *pk = NULL; |
| void *tmpk = NULL; |
| |
| xassert(ctx != NULL); |
| |
| pk = (*(ops.crypto_read_private_key))(path); |
| if (!pk) |
| return SLURM_ERROR; |
| |
| slurm_mutex_lock(&ctx->mutex); |
| |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_CREATOR); |
| |
| tmpk = ctx->key; |
| ctx->key = pk; |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| |
| (*(ops.crypto_destroy_key))(tmpk); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| |
| static int |
| _ctx_update_public_key(slurm_cred_ctx_t ctx, const char *path) |
| { |
| void *pk = NULL; |
| |
| xassert(ctx != NULL); |
| pk = (*(ops.crypto_read_public_key))(path); |
| if (!pk) |
| return SLURM_ERROR; |
| |
| slurm_mutex_lock(&ctx->mutex); |
| |
| xassert(ctx->magic == CRED_CTX_MAGIC); |
| xassert(ctx->type == SLURM_CRED_VERIFIER); |
| |
| if (ctx->exkey) |
| (*(ops.crypto_destroy_key))(ctx->exkey); |
| |
| ctx->exkey = ctx->key; |
| ctx->key = pk; |
| |
| /* |
| * exkey expires in expiry_window seconds plus one minute. |
| * This should be long enough to capture any keys in-flight. |
| */ |
| ctx->exkey_exp = time(NULL) + ctx->expiry_window + 60; |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| return SLURM_SUCCESS; |
| } |
| |
| |
| static bool |
| _exkey_is_valid(slurm_cred_ctx_t ctx) |
| { |
| if (!ctx->exkey) |
| return false; |
| |
| if (time(NULL) > ctx->exkey_exp) { |
| debug2("old job credential key slurmd expired"); |
| (*(ops.crypto_destroy_key))(ctx->exkey); |
| ctx->exkey = NULL; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| static slurm_cred_ctx_t |
| _slurm_cred_ctx_alloc(void) |
| { |
| slurm_cred_ctx_t ctx = xmalloc(sizeof(*ctx)); |
| /* Contents initialized to zero */ |
| |
| slurm_mutex_init(&ctx->mutex); |
| slurm_mutex_lock(&ctx->mutex); |
| |
| ctx->expiry_window = DEFAULT_EXPIRATION_WINDOW; |
| ctx->exkey_exp = (time_t) -1; |
| |
| xassert(ctx->magic = CRED_CTX_MAGIC); |
| |
| slurm_mutex_unlock(&ctx->mutex); |
| return ctx; |
| } |
| |
| static slurm_cred_t * |
| _slurm_cred_alloc(void) |
| { |
| slurm_cred_t *cred = xmalloc(sizeof(*cred)); |
| /* Contents initialized to zero */ |
| |
| slurm_mutex_init(&cred->mutex); |
| cred->uid = (uid_t) -1; |
| |
| xassert(cred->magic = CRED_MAGIC); |
| |
| return cred; |
| } |
| |
| |
| #if EXTREME_DEBUG |
| static void |
| _print_data(char *data, int datalen) |
| { |
| char buf[1024]; |
| size_t len = 0; |
| int i; |
| |
| for (i = 0; i < datalen; i += sizeof(char)) |
| len += sprintf(buf+len, "%02x", data[i]); |
| } |
| #endif |
| |
| static int |
| _slurm_cred_sign(slurm_cred_ctx_t ctx, slurm_cred_t *cred, |
| uint16_t protocol_version) |
| { |
| Buf buffer; |
| int rc; |
| |
| buffer = init_buf(4096); |
| _pack_cred(cred, buffer, protocol_version); |
| rc = (*(ops.crypto_sign))(ctx->key, |
| get_buf_data(buffer), |
| get_buf_offset(buffer), |
| &cred->signature, |
| &cred->siglen); |
| free_buf(buffer); |
| |
| if (rc) { |
| error("Credential sign: %s", |
| (*(ops.crypto_str_error))(rc)); |
| return SLURM_ERROR; |
| } |
| return SLURM_SUCCESS; |
| } |
| |
| static int |
| _slurm_cred_verify_signature(slurm_cred_ctx_t ctx, slurm_cred_t *cred, |
| uint16_t protocol_version) |
| { |
| Buf buffer; |
| int rc; |
| |
| debug("Checking credential with %u bytes of sig data", cred->siglen); |
| buffer = init_buf(4096); |
| _pack_cred(cred, buffer, protocol_version); |
| |
| rc = (*(ops.crypto_verify_sign))(ctx->key, |
| get_buf_data(buffer), |
| get_buf_offset(buffer), |
| cred->signature, |
| cred->siglen); |
| if (rc && _exkey_is_valid(ctx)) { |
| rc = (*(ops.crypto_verify_sign))(ctx->exkey, |
| get_buf_data(buffer), |
| get_buf_offset(buffer), |
| cred->signature, |
| cred->siglen); |
| } |
| free_buf(buffer); |
| |
| if (rc) { |
| error("Credential signature check: %s", |
| (*(ops.crypto_str_error))(rc)); |
| return SLURM_ERROR; |
| } |
| return SLURM_SUCCESS; |
| } |
| |
| |
| static void |
| _pack_cred(slurm_cred_t *cred, Buf buffer, uint16_t protocol_version) |
| { |
| uint32_t cred_uid = (uint32_t) cred->uid; |
| |
| if (protocol_version >= SLURM_14_03_PROTOCOL_VERSION) { |
| pack32(cred->jobid, buffer); |
| pack32(cred->stepid, buffer); |
| pack32(cred_uid, buffer); |
| |
| (void) gres_plugin_job_state_pack(cred->job_gres_list, buffer, |
| cred->jobid, false, |
| SLURM_PROTOCOL_VERSION); |
| gres_plugin_step_state_pack(cred->step_gres_list, buffer, |
| cred->jobid, cred->stepid, |
| SLURM_PROTOCOL_VERSION); |
| pack16(cred->job_core_spec, buffer); |
| pack32(cred->job_mem_limit, buffer); |
| pack32(cred->step_mem_limit, buffer); |
| packstr(cred->step_hostlist, buffer); |
| pack_time(cred->ctime, buffer); |
| #ifndef HAVE_BG |
| { |
| uint32_t tot_core_cnt; |
| tot_core_cnt = bit_size(cred->job_core_bitmap); |
| pack32(tot_core_cnt, buffer); |
| pack_bit_fmt(cred->job_core_bitmap, buffer); |
| pack_bit_fmt(cred->step_core_bitmap, buffer); |
| pack16(cred->core_array_size, buffer); |
| if (cred->core_array_size) { |
| pack16_array(cred->cores_per_socket, |
| cred->core_array_size, |
| buffer); |
| pack16_array(cred->sockets_per_node, |
| cred->core_array_size, |
| buffer); |
| pack32_array(cred->sock_core_rep_count, |
| cred->core_array_size, |
| buffer); |
| } |
| pack32(cred->job_nhosts, buffer); |
| packstr(cred->job_hostlist, buffer); |
| } |
| #endif |
| } else { |
| pack32(cred->jobid, buffer); |
| pack32(cred->stepid, buffer); |
| pack32(cred_uid, buffer); |
| |
| (void) gres_plugin_job_state_pack(cred->job_gres_list, buffer, |
| cred->jobid, false, |
| SLURM_PROTOCOL_VERSION); |
| gres_plugin_step_state_pack(cred->step_gres_list, buffer, |
| cred->jobid, cred->stepid, |
| SLURM_PROTOCOL_VERSION); |
| pack32(cred->job_mem_limit, buffer); |
| pack32(cred->step_mem_limit, buffer); |
| packstr(cred->step_hostlist, buffer); |
| pack_time(cred->ctime, buffer); |
| #ifndef HAVE_BG |
| { |
| uint32_t tot_core_cnt; |
| tot_core_cnt = bit_size(cred->job_core_bitmap); |
| pack32(tot_core_cnt, buffer); |
| pack_bit_fmt(cred->job_core_bitmap, buffer); |
| pack_bit_fmt(cred->step_core_bitmap, buffer); |
| pack16(cred->core_array_size, buffer); |
| if (cred->core_array_size) { |
| pack16_array(cred->cores_per_socket, |
| cred->core_array_size, |
| buffer); |
| pack16_array(cred->sockets_per_node, |
| cred->core_array_size, |
| buffer); |
| pack32_array(cred->sock_core_rep_count, |
| cred->core_array_size, |
| buffer); |
| } |
| pack32(cred->job_nhosts, buffer); |
| packstr(cred->job_hostlist, buffer); |
| } |
| #endif |
| } |
| } |
| |
| |
| static bool |
| _credential_replayed(slurm_cred_ctx_t ctx, slurm_cred_t *cred) |
| { |
| ListIterator i = NULL; |
| cred_state_t *s = NULL; |
| |
| _clear_expired_credential_states(ctx); |
| |
| i = list_iterator_create(ctx->state_list); |
| |
| while ((s = list_next(i))) { |
| if ((s->jobid == cred->jobid) && |
| (s->stepid == cred->stepid) && |
| (s->ctime == cred->ctime)) |
| break; |
| } |
| |
| list_iterator_destroy(i); |
| |
| /* |
| * If we found a match, this credential is being replayed. |
| */ |
| if (s) |
| return true; |
| |
| /* |
| * Otherwise, save the credential state |
| */ |
| _insert_cred_state(ctx, cred); |
| return false; |
| } |
| |
| #ifdef DISABLE_LOCALTIME |
| extern char * timestr (const time_t *tp, char *buf, size_t n) |
| #else |
| static char * timestr (const time_t *tp, char *buf, size_t n) |
| #endif |
| { |
| char fmt[] = "%y%m%d%H%M%S"; |
| struct tm tmval; |
| #ifdef DISABLE_LOCALTIME |
| static int disabled = 0; |
| if (buf == NULL) |
| disabled = 1; |
| if (disabled) |
| return NULL; |
| #endif |
| if (!localtime_r (tp, &tmval)) |
| error ("localtime_r: %m"); |
| slurm_strftime (buf, n, fmt, &tmval); |
| return (buf); |
| } |
| |
| extern void |
| slurm_cred_handle_reissue(slurm_cred_ctx_t ctx, slurm_cred_t *cred) |
| { |
| job_state_t *j = _find_job_state(ctx, cred->jobid); |
| |
| if (j != NULL && j->revoked && (cred->ctime > j->revoked)) { |
| /* The credential has been reissued. Purge the |
| * old record so that "cred" will look like a new |
| * credential to any ensuing commands. */ |
| info("reissued job credential for job %u", j->jobid); |
| |
| /* Setting j->expiration to zero will make |
| * _clear_expired_job_states() remove this |
| * job credential from the cred context. */ |
| j->expiration = 0; |
| _clear_expired_job_states(ctx); |
| } |
| } |
| |
| extern bool |
| slurm_cred_revoked(slurm_cred_ctx_t ctx, slurm_cred_t *cred) |
| { |
| job_state_t *j = _find_job_state(ctx, cred->jobid); |
| |
| if ((j == NULL) || (j->revoked == (time_t)0)) |
| return false; |
| |
| if (cred->ctime <= j->revoked) |
| return true; |
| |
| return false; |
| } |
| |
| static bool |
| _credential_revoked(slurm_cred_ctx_t ctx, slurm_cred_t *cred) |
| { |
| job_state_t *j = NULL; |
| |
| _clear_expired_job_states(ctx); |
| |
| if (!(j = _find_job_state(ctx, cred->jobid))) { |
| (void) _insert_job_state(ctx, cred->jobid); |
| return false; |
| } |
| |
| if (cred->ctime <= j->revoked) { |
| #if DEBUG_TIME |
| char buf[64]; |
| debug3("cred for %u revoked. expires at %s", |
| j->jobid, timestr(&j->expiration, buf, 64)); |
| #else |
| debug3("cred for %u revoked. expires at %"PRIu64" UTS", |
| j->jobid, (uint64_t) j->expiration); |
| #endif |
| return true; |
| } |
| |
| return false; |
| } |
| |
| |
| static job_state_t * |
| _find_job_state(slurm_cred_ctx_t ctx, uint32_t jobid) |
| { |
| ListIterator i = NULL; |
| job_state_t *j = NULL; |
| |
| i = list_iterator_create(ctx->job_list); |
| while ((j = list_next(i))) { |
| if (j->jobid == jobid) |
| break; |
| } |
| list_iterator_destroy(i); |
| return j; |
| } |
| |
| static int |
| _find_cred_state(cred_state_t *c, slurm_cred_t *cred) |
| { |
| return ((c->jobid == cred->jobid) && (c->stepid == cred->stepid) && |
| (c->ctime == cred->ctime)); |
| } |
| |
| static job_state_t * |
| _insert_job_state(slurm_cred_ctx_t ctx, uint32_t jobid) |
| { |
| job_state_t *j = _job_state_create(jobid); |
| list_append(ctx->job_list, j); |
| return j; |
| } |
| |
| |
| static job_state_t * |
| _job_state_create(uint32_t jobid) |
| { |
| job_state_t *j = xmalloc(sizeof(*j)); |
| |
| j->jobid = jobid; |
| j->revoked = (time_t) 0; |
| j->ctime = time(NULL); |
| j->expiration = (time_t) MAX_TIME; |
| |
| return j; |
| } |
| |
| static void |
| _job_state_destroy(job_state_t *j) |
| { |
| debug3 ("destroying job %u state", j->jobid); |
| xfree(j); |
| } |
| |
| |
| static void |
| _clear_expired_job_states(slurm_cred_ctx_t ctx) |
| { |
| static time_t last_scan = 0; |
| time_t now = time(NULL); |
| ListIterator i = NULL; |
| job_state_t *j = NULL; |
| |
| if ((now - last_scan) < 2) /* Reduces slurmd overhead */ |
| return; |
| last_scan = now; |
| |
| i = list_iterator_create(ctx->job_list); |
| while ((j = list_next(i))) { |
| #if DEBUG_TIME |
| char t1[64], t2[64], t3[64]; |
| if (j->revoked) { |
| strcpy(t2, " revoked:"); |
| timestr(&j->revoked, (t2+9), (64-9)); |
| } else { |
| t2[0] = '\0'; |
| } |
| if (j->expiration) { |
| strcpy(t3, " expires:"); |
| timestr(&j->revoked, (t3+9), (64-9)); |
| } else { |
| t3[0] = '\0'; |
| } |
| debug3("state for jobid %u: ctime:%s%s%s", |
| j->jobid, timestr(&j->ctime, t1, 64), t2, t3); |
| #else |
| debug3("state for jobid %u: ctime:%"PRIu64" revoked:%"PRIu64" " |
| "expires:%"PRIu64"", |
| j->jobid, (uint64_t)j->ctime, (uint64_t)j->revoked, |
| (uint64_t)j->revoked); |
| #endif |
| if (j->revoked && (now > j->expiration)) { |
| list_delete_item(i); |
| } |
| } |
| |
| list_iterator_destroy(i); |
| } |
| |
| |
| static void |
| _clear_expired_credential_states(slurm_cred_ctx_t ctx) |
| { |
| static time_t last_scan = 0; |
| time_t now = time(NULL); |
| ListIterator i = NULL; |
| cred_state_t *s = NULL; |
| |
| if ((now - last_scan) < 2) /* Reduces slurmd overhead */ |
| return; |
| last_scan = now; |
| |
| i = list_iterator_create(ctx->state_list); |
| while ((s = list_next(i))) { |
| if (now > s->expiration) |
| list_delete_item(i); |
| } |
| list_iterator_destroy(i); |
| } |
| |
| |
| static void |
| _insert_cred_state(slurm_cred_ctx_t ctx, slurm_cred_t *cred) |
| { |
| cred_state_t *s = _cred_state_create(ctx, cred); |
| list_append(ctx->state_list, s); |
| } |
| |
| |
| static cred_state_t * |
| _cred_state_create(slurm_cred_ctx_t ctx, slurm_cred_t *cred) |
| { |
| cred_state_t *s = xmalloc(sizeof(*s)); |
| |
| s->jobid = cred->jobid; |
| s->stepid = cred->stepid; |
| s->ctime = cred->ctime; |
| s->expiration = cred->ctime + ctx->expiry_window; |
| |
| return s; |
| } |
| |
| static void |
| _cred_state_destroy(cred_state_t *s) |
| { |
| xfree(s); |
| } |
| |
| |
| static void |
| _cred_state_pack_one(cred_state_t *s, Buf buffer) |
| { |
| pack32(s->jobid, buffer); |
| pack32(s->stepid, buffer); |
| pack_time(s->ctime, buffer); |
| pack_time(s->expiration, buffer); |
| } |
| |
| |
| static cred_state_t * |
| _cred_state_unpack_one(Buf buffer) |
| { |
| cred_state_t *s = xmalloc(sizeof(*s)); |
| |
| safe_unpack32(&s->jobid, buffer); |
| safe_unpack32(&s->stepid, buffer); |
| safe_unpack_time(&s->ctime, buffer); |
| safe_unpack_time(&s->expiration, buffer); |
| return s; |
| |
| unpack_error: |
| _cred_state_destroy(s); |
| return NULL; |
| } |
| |
| |
| static void |
| _job_state_pack_one(job_state_t *j, Buf buffer) |
| { |
| pack32(j->jobid, buffer); |
| pack_time(j->revoked, buffer); |
| pack_time(j->ctime, buffer); |
| pack_time(j->expiration, buffer); |
| } |
| |
| |
| static job_state_t * |
| _job_state_unpack_one(Buf buffer) |
| { |
| char t1[64], t2[64], t3[64]; |
| job_state_t *j = xmalloc(sizeof(*j)); |
| |
| safe_unpack32( &j->jobid, buffer); |
| safe_unpack_time( &j->revoked, buffer); |
| safe_unpack_time( &j->ctime, buffer); |
| safe_unpack_time( &j->expiration, buffer); |
| |
| if (j->revoked) { |
| strcpy(t2, " revoked:"); |
| timestr(&j->revoked, (t2+9), (64-9)); |
| } else { |
| t2[0] = '\0'; |
| } |
| if (j->expiration) { |
| strcpy(t3, " expires:"); |
| timestr(&j->revoked, (t3+9), (64-9)); |
| } else { |
| t3[0] = '\0'; |
| } |
| debug3("cred_unpack: job %u ctime:%s%s%s", |
| j->jobid, timestr (&j->ctime, t1, 64), t2, t3); |
| |
| if ((j->revoked) && (j->expiration == (time_t) MAX_TIME)) { |
| info ("Warning: revoke on job %u has no expiration", |
| j->jobid); |
| j->expiration = j->revoked + 600; |
| } |
| |
| return j; |
| |
| unpack_error: |
| _job_state_destroy(j); |
| return NULL; |
| } |
| |
| |
| static void |
| _cred_state_pack(slurm_cred_ctx_t ctx, Buf buffer) |
| { |
| ListIterator i = NULL; |
| cred_state_t *s = NULL; |
| |
| pack32(list_count(ctx->state_list), buffer); |
| |
| i = list_iterator_create(ctx->state_list); |
| while ((s = list_next(i))) |
| _cred_state_pack_one(s, buffer); |
| list_iterator_destroy(i); |
| } |
| |
| |
| static void |
| _cred_state_unpack(slurm_cred_ctx_t ctx, Buf buffer) |
| { |
| time_t now = time(NULL); |
| uint32_t n; |
| int i = 0; |
| cred_state_t *s = NULL; |
| |
| safe_unpack32(&n, buffer); |
| |
| for (i = 0; i < n; i++) { |
| if (!(s = _cred_state_unpack_one(buffer))) |
| goto unpack_error; |
| |
| if (now < s->expiration) |
| list_append(ctx->state_list, s); |
| else |
| _cred_state_destroy(s); |
| } |
| |
| return; |
| |
| unpack_error: |
| error("Unable to unpack job credential state information"); |
| return; |
| } |
| |
| |
| static void |
| _job_state_pack(slurm_cred_ctx_t ctx, Buf buffer) |
| { |
| ListIterator i = NULL; |
| job_state_t *j = NULL; |
| |
| pack32((uint32_t) list_count(ctx->job_list), buffer); |
| |
| |
| i = list_iterator_create(ctx->job_list); |
| while ((j = list_next(i))) |
| _job_state_pack_one(j, buffer); |
| list_iterator_destroy(i); |
| } |
| |
| |
| static void |
| _job_state_unpack(slurm_cred_ctx_t ctx, Buf buffer) |
| { |
| time_t now = time(NULL); |
| uint32_t n = 0; |
| int i = 0; |
| job_state_t *j = NULL; |
| |
| safe_unpack32(&n, buffer); |
| |
| for (i = 0; i < n; i++) { |
| if (!(j = _job_state_unpack_one(buffer))) |
| goto unpack_error; |
| |
| if (!j->revoked || (j->revoked && (now < j->expiration))) |
| list_append(ctx->job_list, j); |
| else { |
| debug3 ("not appending expired job %u state", |
| j->jobid); |
| _job_state_destroy(j); |
| } |
| } |
| |
| return; |
| |
| unpack_error: |
| error("Unable to unpack job state information"); |
| return; |
| } |
| |
| /*****************************************************************************\ |
| ***************** SBCAST CREDENTIAL FUNCTIONS ****************** |
| \*****************************************************************************/ |
| |
| /* Pack sbcast credential without the digital signature */ |
| static void _pack_sbcast_cred(sbcast_cred_t *sbcast_cred, Buf buffer) |
| { |
| pack_time(sbcast_cred->ctime, buffer); |
| pack_time(sbcast_cred->expiration, buffer); |
| pack32(sbcast_cred->jobid, buffer); |
| packstr(sbcast_cred->nodes, buffer); |
| } |
| |
| /* Create an sbcast credential for the specified job and nodes |
| * including digital signature. |
| * RET the sbcast credential or NULL on error */ |
| sbcast_cred_t *create_sbcast_cred(slurm_cred_ctx_t ctx, |
| uint32_t job_id, char *nodes, |
| time_t expiration) |
| { |
| Buf buffer; |
| int rc; |
| sbcast_cred_t *sbcast_cred; |
| time_t now = time(NULL); |
| |
| xassert(ctx); |
| if (_slurm_crypto_init() < 0) |
| return NULL; |
| |
| sbcast_cred = xmalloc(sizeof(struct sbcast_cred)); |
| sbcast_cred->ctime = now; |
| sbcast_cred->expiration = expiration; |
| sbcast_cred->jobid = job_id; |
| sbcast_cred->nodes = xstrdup(nodes); |
| |
| buffer = init_buf(4096); |
| _pack_sbcast_cred(sbcast_cred, buffer); |
| rc = (*(ops.crypto_sign))( |
| ctx->key, get_buf_data(buffer), get_buf_offset(buffer), |
| &sbcast_cred->signature, &sbcast_cred->siglen); |
| free_buf(buffer); |
| |
| if (rc) { |
| error("sbcast_cred sign: %s", |
| (*(ops.crypto_str_error))(rc)); |
| delete_sbcast_cred(sbcast_cred); |
| return NULL; |
| } |
| |
| return sbcast_cred; |
| } |
| |
| /* Delete an sbcast credential created using create_sbcast_cred() or |
| * unpack_sbcast_cred() */ |
| void delete_sbcast_cred(sbcast_cred_t *sbcast_cred) |
| { |
| if (sbcast_cred) { |
| xfree(sbcast_cred->nodes); |
| xfree(sbcast_cred->signature); |
| xfree(sbcast_cred); |
| } |
| } |
| |
| static void _sbast_cache_add(sbcast_cred_t *sbcast_cred) |
| { |
| int i; |
| uint32_t sig_num = 0; |
| struct sbcast_cache *new_cache_rec; |
| |
| /* Using two bytes at a time gives us a larger number |
| * and reduces the possibility of a duplicate value */ |
| for (i = 0; i < sbcast_cred->siglen; i += 2) { |
| sig_num += (sbcast_cred->signature[i] << 8) + |
| sbcast_cred->signature[i+1]; |
| } |
| |
| new_cache_rec = xmalloc(sizeof(struct sbcast_cache)); |
| new_cache_rec->expire = sbcast_cred->expiration; |
| new_cache_rec->value = sig_num; |
| list_append(sbcast_cache_list, new_cache_rec); |
| } |
| |
| static void _sbcast_cache_del(void *x) |
| { |
| xfree(x); |
| } |
| |
| /* Extract contents of an sbcast credential verifying the digital signature. |
| * NOTE: We can only perform the full credential validation once with |
| * Munge without generating a credential replay error, so we only |
| * verify the credential for block one. All others must have a |
| * recent signature on file (in our cache) or the slurmd must have |
| * recently been restarted. |
| * RET 0 on success, -1 on error */ |
| int extract_sbcast_cred(slurm_cred_ctx_t ctx, |
| sbcast_cred_t *sbcast_cred, uint16_t block_no, |
| uint32_t *job_id, char **nodes) |
| { |
| struct sbcast_cache *next_cache_rec; |
| uint32_t sig_num = 0; |
| int i, rc; |
| time_t now = time(NULL); |
| Buf buffer; |
| |
| *job_id = 0xffffffff; |
| *nodes = NULL; |
| xassert(ctx); |
| |
| if (_slurm_crypto_init() < 0) |
| return -1; |
| |
| if (now > sbcast_cred->expiration) |
| return -1; |
| |
| if (block_no == 1) { |
| buffer = init_buf(4096); |
| _pack_sbcast_cred(sbcast_cred, buffer); |
| /* NOTE: the verification checks that the credential was |
| * created by SlurmUser or root */ |
| rc = (*(ops.crypto_verify_sign)) ( |
| ctx->key, get_buf_data(buffer), get_buf_offset(buffer), |
| sbcast_cred->signature, sbcast_cred->siglen); |
| free_buf(buffer); |
| |
| if (rc) { |
| error("sbcast_cred verify: %s", |
| (*(ops.crypto_str_error))(rc)); |
| return -1; |
| } |
| _sbast_cache_add(sbcast_cred); |
| |
| } else { |
| char *err_str = NULL; |
| bool cache_match_found = false; |
| ListIterator sbcast_iter; |
| for (i = 0; i < sbcast_cred->siglen; i += 2) { |
| sig_num += (sbcast_cred->signature[i] << 8) + |
| sbcast_cred->signature[i+1]; |
| } |
| |
| sbcast_iter = list_iterator_create(sbcast_cache_list); |
| while ((next_cache_rec = |
| (struct sbcast_cache *) list_next(sbcast_iter))) { |
| if ((next_cache_rec->expire == sbcast_cred->expiration) && |
| (next_cache_rec->value == sig_num)) { |
| cache_match_found = true; |
| break; |
| } |
| if (next_cache_rec->expire <= now) |
| list_delete_item(sbcast_iter); |
| } |
| list_iterator_destroy(sbcast_iter); |
| |
| if (!cache_match_found) { |
| error("sbcast_cred verify: signature not in cache"); |
| if (SLURM_DIFFTIME(now, crypto_restart_time) > 60) |
| return -1; /* restarted >60 secs ago */ |
| buffer = init_buf(4096); |
| _pack_sbcast_cred(sbcast_cred, buffer); |
| rc = (*(ops.crypto_verify_sign)) ( |
| ctx->key, get_buf_data(buffer), |
| get_buf_offset(buffer), |
| sbcast_cred->signature, sbcast_cred->siglen); |
| free_buf(buffer); |
| if (rc) |
| err_str = (char *)(*(ops.crypto_str_error))(rc); |
| if (err_str && strcmp(err_str, "Credential replayed")) { |
| error("sbcast_cred verify: %s", err_str); |
| return -1; |
| } |
| info("sbcast_cred verify: signature revalidated"); |
| _sbast_cache_add(sbcast_cred); |
| } |
| } |
| |
| *job_id = sbcast_cred->jobid; |
| *nodes = xstrdup(sbcast_cred->nodes); |
| return 0; |
| } |
| |
| /* Pack an sbcast credential into a buffer including the digital signature */ |
| void pack_sbcast_cred(sbcast_cred_t *sbcast_cred, Buf buffer) |
| { |
| static int bad_cred_test = -1; |
| xassert(sbcast_cred); |
| xassert(sbcast_cred->siglen > 0); |
| |
| _pack_sbcast_cred(sbcast_cred, buffer); |
| if (bad_cred_test == -1) { |
| char *sbcast_env = getenv("SLURM_SBCAST_AUTH_FAIL_TEST"); |
| if (sbcast_env) |
| bad_cred_test = atoi(sbcast_env); |
| else |
| bad_cred_test = 0; |
| } |
| if (bad_cred_test > 0) { |
| int i = ((int) time(NULL)) % sbcast_cred->siglen; |
| char save_sig = sbcast_cred->signature[i]; |
| sbcast_cred->signature[i]++; |
| packmem(sbcast_cred->signature, sbcast_cred->siglen, buffer); |
| sbcast_cred->signature[i] = save_sig; |
| } else { |
| packmem(sbcast_cred->signature, sbcast_cred->siglen, buffer); |
| } |
| } |
| |
| /* Pack an sbcast credential into a buffer including the digital signature */ |
| sbcast_cred_t *unpack_sbcast_cred(Buf buffer) |
| { |
| uint32_t len; |
| sbcast_cred_t *sbcast_cred; |
| uint32_t uint32_tmp; |
| |
| sbcast_cred = xmalloc(sizeof(struct sbcast_cred)); |
| safe_unpack_time(&sbcast_cred->ctime, buffer); |
| safe_unpack_time(&sbcast_cred->expiration, buffer); |
| safe_unpack32(&sbcast_cred->jobid, buffer); |
| safe_unpackstr_xmalloc(&sbcast_cred->nodes, &uint32_tmp, buffer); |
| |
| /* "sigp" must be last */ |
| safe_unpackmem_xmalloc(&sbcast_cred->signature, &len, buffer); |
| sbcast_cred->siglen = len; |
| xassert(len > 0); |
| |
| return sbcast_cred; |
| |
| unpack_error: |
| delete_sbcast_cred(sbcast_cred); |
| return NULL; |
| } |
| |
| void print_sbcast_cred(sbcast_cred_t *sbcast_cred) |
| { |
| info("Sbcast_cred: Jobid %u", sbcast_cred->jobid ); |
| info("Sbcast_cred: Nodes %s", sbcast_cred->nodes ); |
| info("Sbcast_cred: ctime %s", slurm_ctime(&sbcast_cred->ctime) ); |
| info("Sbcast_cred: Expire %s", slurm_ctime(&sbcast_cred->expiration) ); |
| } |