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