1 /*****************************************************************************\
2 * slurm_auth.c - implementation-independent authentication API definitions
3 *****************************************************************************
4 * Copyright (C) 2002-2007 The Regents of the University of California.
5 * Copyright (C) 2008-2009 Lawrence Livermore National Security.
6 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
7 * Written by Jay Windley <jwindley@lnxi.com>
8 * CODE-OCEC-09-009. All rights reserved.
9 *
10 * This file is part of Slurm, a resource management program.
11 * For details, see <https://slurm.schedmd.com/>.
12 * Please also read the included file: DISCLAIMER.
13 *
14 * Slurm is free software; you can redistribute it and/or modify it under
15 * the terms of the GNU General Public License as published by the Free
16 * Software Foundation; either version 2 of the License, or (at your option)
17 * any later version.
18 *
19 * In addition, as a special exception, the copyright holders give permission
20 * to link the code of portions of this program with the OpenSSL library under
21 * certain conditions as described in each individual source file, and
22 * distribute linked combinations including the two. You must obey the GNU
23 * General Public License in all respects for all of the code used other than
24 * OpenSSL. If you modify file(s) with this exception, you may extend this
25 * exception to your version of the file(s), but you are not obligated to do
26 * so. If you do not wish to do so, delete this exception statement from your
27 * version. If you delete this exception statement from all source files in
28 * the program, then also delete it here.
29 *
30 * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
31 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
32 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
33 * details.
34 *
35 * You should have received a copy of the GNU General Public License along
36 * with Slurm; if not, write to the Free Software Foundation, Inc.,
37 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
38 \*****************************************************************************/
39
40 #include <stdlib.h>
41 #include <string.h>
42
43 #include <pthread.h>
44
45 #include "src/common/macros.h"
46 #include "src/common/plugin.h"
47 #include "src/common/plugrack.h"
48 #include "src/common/read_config.h"
49 #include "src/common/slurm_auth.h"
50 #include "src/common/slurm_protocol_api.h"
51 #include "src/common/xassert.h"
52 #include "src/common/xmalloc.h"
53 #include "src/common/xstring.h"
54
55 static bool init_run = false;
56
57 typedef struct {
58 int index;
59 char data[];
60 } cred_wrapper_t;
61
62 typedef struct {
63 uint32_t (*plugin_id);
64 char (*plugin_type);
65 void * (*create) (char *auth_info);
66 int (*destroy) (void *cred);
67 int (*verify) (void *cred, char *auth_info);
68 uid_t (*get_uid) (void *cred);
69 gid_t (*get_gid) (void *cred);
70 char * (*get_host) (void *cred);
71 int (*pack) (void *cred, Buf buf,
72 uint16_t protocol_version);
73 void * (*unpack) (Buf buf, uint16_t protocol_version);
74 int (*thread_config) (const char *token, const char *username);
75 void (*thread_clear) (void);
76 char * (*token_generate) (const char *username, int lifespan);
77 } slurm_auth_ops_t;
78 /*
79 * These strings must be kept in the same order as the fields
80 * declared for slurm_auth_ops_t.
81 */
82 static const char *syms[] = {
83 "plugin_id",
84 "plugin_type",
85 "slurm_auth_create",
86 "slurm_auth_destroy",
87 "slurm_auth_verify",
88 "slurm_auth_get_uid",
89 "slurm_auth_get_gid",
90 "slurm_auth_get_host",
91 "slurm_auth_pack",
92 "slurm_auth_unpack",
93 "slurm_auth_thread_config",
94 "slurm_auth_thread_clear",
95 "slurm_auth_token_generate",
96 };
97
98 /*
99 * A global authentication context. "Global" in the sense that there's
100 * only one, with static bindings. We don't export it.
101 */
102 static slurm_auth_ops_t *ops = NULL;
103 static plugin_context_t **g_context = NULL;
104 static int g_context_num = -1;
105 static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER;
106
slurm_auth_init(char * auth_type)107 extern int slurm_auth_init(char *auth_type)
108 {
109 int retval = SLURM_SUCCESS;
110 char *auth_alt_types = NULL, *list = NULL;
111 char *auth_plugin_type = NULL, *type, *last = NULL;
112 char *plugin_type = "auth";
113 static bool daemon_run = false, daemon_set = false;
114
115 if (init_run && (g_context_num > 0))
116 return retval;
117
118 slurm_mutex_lock(&context_lock);
119
120 if (g_context_num > 0)
121 goto done;
122
123 if (getenv("SLURM_JWT")) {
124 slurm_set_auth_type("auth/jwt");
125 } else if (auth_type)
126 slurm_set_auth_type(auth_type);
127
128 type = auth_plugin_type = slurm_get_auth_type();
129 if (run_in_daemon(&daemon_run, &daemon_set, "slurmctld,slurmdbd"))
130 list = auth_alt_types = slurm_get_auth_alt_types();
131 g_context_num = 0;
132 if (!auth_plugin_type || auth_plugin_type[0] == '\0')
133 goto done;
134
135 /*
136 * This loop construct ensures that the AuthType is in position zero
137 * of the ops and g_context arrays, followed by any AuthAltTypes that
138 * have been defined. This ensures that the most common type is found
139 * first in g_slurm_auth_unpack(), and that we can default to
140 * the zeroth element rather than tracking the primary plugin
141 * through some other index.
142 * One other side effect is that the AuthAltTypes are permitted to
143 * be comma separated, vs. AuthType which can have only one value.
144 */
145 while (type) {
146 xrecalloc(ops, g_context_num + 1, sizeof(slurm_auth_ops_t));
147 xrecalloc(g_context, g_context_num + 1,
148 sizeof(plugin_context_t));
149
150 g_context[g_context_num] = plugin_context_create(
151 plugin_type, type, (void **)&ops[g_context_num],
152 syms, sizeof(syms));
153
154 if (!g_context[g_context_num]) {
155 error("cannot create %s context for %s", plugin_type, type);
156 retval = SLURM_ERROR;
157 goto done;
158 }
159 g_context_num++;
160
161 if (auth_alt_types) {
162 type = strtok_r(list, ",", &last);
163 list = NULL; /* for next iteration */
164 } else {
165 type = NULL;
166 }
167 }
168 init_run = true;
169
170 done:
171 xfree(auth_plugin_type);
172 xfree(auth_alt_types);
173 slurm_mutex_unlock(&context_lock);
174 return retval;
175 }
176
177 /* Release all global memory associated with the plugin */
slurm_auth_fini(void)178 extern int slurm_auth_fini(void)
179 {
180 int i, rc = SLURM_SUCCESS, rc2;
181
182 slurm_mutex_lock(&context_lock);
183 if (!g_context)
184 goto done;
185
186 init_run = false;
187
188 for (i = 0; i < g_context_num; i++) {
189 rc2 = plugin_context_destroy(g_context[i]);
190 if (rc2) {
191 debug("%s: %s: %s",
192 __func__, g_context[i]->type,
193 slurm_strerror(rc2));
194 rc = SLURM_ERROR;
195 }
196 }
197
198 xfree(ops);
199 xfree(g_context);
200 g_context_num = -1;
201
202 done:
203 slurm_mutex_unlock(&context_lock);
204 return rc;
205 }
206
207 /*
208 * Retrieve the auth_index corresponding to the authentication
209 * plugin used to create a given credential.
210 *
211 * Note that this works because all plugin credential types
212 * are required to store the auth_index as an int first in their
213 * internal (opaque) structures.
214 *
215 * The cast through cred_wrapper_t then gives us convenient access
216 * to that auth_index value.
217 */
slurm_auth_index(void * cred)218 int slurm_auth_index(void *cred)
219 {
220 cred_wrapper_t *wrapper = (cred_wrapper_t *) cred;
221
222 if (wrapper)
223 return wrapper->index;
224
225 return 0;
226 }
227
228 /*
229 * Static bindings for the global authentication context. The test
230 * of the function pointers is omitted here because the global
231 * context initialization includes a test for the completeness of
232 * the API function dispatcher.
233 */
234
g_slurm_auth_create(int index,char * auth_info)235 void *g_slurm_auth_create(int index, char *auth_info)
236 {
237 cred_wrapper_t *cred;
238
239 if (slurm_auth_init(NULL) < 0)
240 return NULL;
241
242 cred = (*(ops[index].create))(auth_info);
243 if (cred)
244 cred->index = index;
245 return cred;
246 }
247
g_slurm_auth_destroy(void * cred)248 int g_slurm_auth_destroy(void *cred)
249 {
250 cred_wrapper_t *wrap = (cred_wrapper_t *) cred;
251
252 if (!wrap || slurm_auth_init(NULL) < 0)
253 return SLURM_ERROR;
254
255 return (*(ops[wrap->index].destroy))(cred);
256 }
257
g_slurm_auth_verify(void * cred,char * auth_info)258 int g_slurm_auth_verify(void *cred, char *auth_info)
259 {
260 cred_wrapper_t *wrap = (cred_wrapper_t *) cred;
261
262 if (!wrap || slurm_auth_init(NULL) < 0)
263 return SLURM_ERROR;
264
265 return (*(ops[wrap->index].verify))(cred, auth_info);
266 }
267
g_slurm_auth_get_uid(void * cred)268 uid_t g_slurm_auth_get_uid(void *cred)
269 {
270 cred_wrapper_t *wrap = (cred_wrapper_t *) cred;
271
272 if (!wrap || slurm_auth_init(NULL) < 0)
273 return SLURM_AUTH_NOBODY;
274
275 return (*(ops[wrap->index].get_uid))(cred);
276 }
277
g_slurm_auth_get_gid(void * cred)278 gid_t g_slurm_auth_get_gid(void *cred)
279 {
280 cred_wrapper_t *wrap = (cred_wrapper_t *) cred;
281
282 if (!wrap || slurm_auth_init(NULL) < 0)
283 return SLURM_AUTH_NOBODY;
284
285 return (*(ops[wrap->index].get_gid))(cred);
286 }
287
g_slurm_auth_get_host(void * cred)288 char *g_slurm_auth_get_host(void *cred)
289 {
290 cred_wrapper_t *wrap = (cred_wrapper_t *) cred;
291
292 if (!wrap || slurm_auth_init(NULL) < 0)
293 return NULL;
294
295 return (*(ops[wrap->index].get_host))(cred);
296 }
297
g_slurm_auth_pack(void * cred,Buf buf,uint16_t protocol_version)298 int g_slurm_auth_pack(void *cred, Buf buf, uint16_t protocol_version)
299 {
300 cred_wrapper_t *wrap = (cred_wrapper_t *) cred;
301
302 if (!wrap || slurm_auth_init(NULL) < 0)
303 return SLURM_ERROR;
304
305 if (protocol_version >= SLURM_19_05_PROTOCOL_VERSION) {
306 pack32(*ops[wrap->index].plugin_id, buf);
307 return (*(ops[wrap->index].pack))(cred, buf, protocol_version);
308 } else if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
309 packstr(ops[wrap->index].plugin_type, buf);
310 /*
311 * This next field was packed with plugin_version within each
312 * individual auth plugin, but upon unpack was never checked
313 * against anything. Rather than expose the protocol_version
314 * symbol, just pack a zero here instead.
315 */
316 pack32(0, buf);
317 return (*(ops[wrap->index].pack))(cred, buf, protocol_version);
318 } else {
319 error("%s: protocol_version %hu not supported",
320 __func__, protocol_version);
321 return SLURM_ERROR;
322 }
323 }
324
g_slurm_auth_unpack(Buf buf,uint16_t protocol_version)325 void *g_slurm_auth_unpack(Buf buf, uint16_t protocol_version)
326 {
327 uint32_t plugin_id = 0;
328 cred_wrapper_t *cred;
329
330 if (!buf || slurm_auth_init(NULL) < 0)
331 return NULL;
332
333 if (protocol_version >= SLURM_19_05_PROTOCOL_VERSION) {
334 safe_unpack32(&plugin_id, buf);
335 for (int i = 0; i < g_context_num; i++) {
336 if (plugin_id == *(ops[i].plugin_id)) {
337 cred = (*(ops[i].unpack))(buf,
338 protocol_version);
339 if (cred)
340 cred->index = i;
341 return cred;
342 }
343 }
344 error("%s: remote plugin_id %u not found",
345 __func__, plugin_id);
346 return NULL;
347 } else if (protocol_version >= SLURM_MIN_PROTOCOL_VERSION) {
348 char *plugin_type;
349 uint32_t uint32_tmp, version;
350 safe_unpackmem_ptr(&plugin_type, &uint32_tmp, buf);
351 safe_unpack32(&version, buf);
352 for (int i = 0; i < g_context_num; i++) {
353 if (!xstrcmp(plugin_type, ops[i].plugin_type)) {
354 cred = (*(ops[i].unpack))(buf,
355 protocol_version);
356 if (cred)
357 cred->index = i;
358 return cred;
359 }
360 }
361 error("%s: remote plugin_type %s not found",
362 __func__, plugin_type);
363 return NULL;
364 } else {
365 error("%s: protocol_version %hu not supported",
366 __func__, protocol_version);
367 return NULL;
368 }
369
370 unpack_error:
371 return NULL;
372 }
373
g_slurm_auth_thread_config(const char * token,const char * username)374 int g_slurm_auth_thread_config(const char *token, const char *username)
375 {
376 if (slurm_auth_init(NULL) < 0)
377 return SLURM_ERROR;
378
379 return (*(ops[0].thread_config))(token, username);
380 }
381
g_slurm_auth_thread_clear(void)382 void g_slurm_auth_thread_clear(void)
383 {
384 if (slurm_auth_init(NULL) < 0)
385 return;
386
387 (*(ops[0].thread_clear))();
388 }
389
g_slurm_auth_token_generate(int plugin_id,const char * username,int lifespan)390 char *g_slurm_auth_token_generate(int plugin_id, const char *username,
391 int lifespan)
392 {
393 if (slurm_auth_init(NULL) < 0)
394 return NULL;
395
396 for (int i = 0; i < g_context_num; i++) {
397 if (plugin_id == *(ops[i].plugin_id)) {
398 return (*(ops[i].token_generate))(username, lifespan);
399 }
400 }
401
402 return NULL;
403 }
404