File: misc.c

package info (click to toggle)
libuser 1%3A0.64~dfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 4,092 kB
  • sloc: ansic: 16,599; python: 2,555; sh: 877; yacc: 782; makefile: 235; xml: 106
file content (358 lines) | stat: -rw-r--r-- 10,403 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/* Copyright (C) 2000-2002, 2004 Red Hat, Inc.
 *
 * This is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Library General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "user_private.h"
#include "internal.h"

/**
 * SECTION:value
 * @short_description: Simplified interface to GValue types used in libuser
 * entities.
 * @include: libuser/user.h
 *
 * Libuser entities store attribute values as #GValue, which allows representing
 * any possible data type.  Only a few types are needed in practice; the only
 * types applications should hard-code are %G_TYPE_LONG and %G_TYPE_STRING
 * (%G_TYPE_STRING can usually be used as a fallback for other number types).
 *
 * The only currently used data types that are not conveniently supported using
 * the above types are #uid_t and #gid_t (which can be together represented in
 * #id_t), because they can support values outside of the range of #glong.
 * Helper functions are provided to convert values between #id_t and #GValue,
 * even if the value is stored using %G_TYPE_STRING.  The #GValue types used
 * for storing #id_t values are an internal implementation detail of libuser
 * and applications should not rely on them.
 *
 * Values of each attribute are expected to have a specific type, documented in
 * the documentation of the specific attribute name.  Using other types (e.g.
 * using %G_TYPE_STRING for %LU_UIDNUMBER) is not allowed and results in
 * undefined behavior.  You can use lu_value_strdup() and
 * lu_value_init_set_attr_from_string() for conversion between strings and
 * values appropriate for a specific attribute.
 */

/**
 * lu_value_strdup:
 * @value: #GValue
 *
 * Converts @value, of any type used by libuser, to a string.  Preferable to
 * hard-coding checks for expected value types.
 *
 * Returns: string, should be freed by g_free()
 */
char *
lu_value_strdup(const GValue *value)
{
	char *ret;

	if (G_VALUE_HOLDS_STRING(value))
		ret = g_value_dup_string(value);
	else if (G_VALUE_HOLDS_LONG(value))
		ret = g_strdup_printf("%ld", g_value_get_long(value));
	else if (G_VALUE_HOLDS_INT64(value))
		ret = g_strdup_printf("%lld",
				      (long long)g_value_get_int64(value));
	else {
		g_assert_not_reached();
		ret = NULL;
	}
	return ret;
}

/**
 * lu_values_equal:
 * @a: #GValue
 * @b: #GValue
 *
 * Check whether @a and @b have the same type and value.
 *
 * Returns: #TRUE if @a and @b have the same type and value
 */
int
lu_values_equal(const GValue *a, const GValue *b)
{
	g_return_val_if_fail(G_VALUE_TYPE(a) == G_VALUE_TYPE(b), FALSE);
	if (G_VALUE_HOLDS_STRING(a))
		return strcmp(g_value_get_string(a), g_value_get_string(b))
			== 0;
	else if (G_VALUE_HOLDS_LONG(a))
		return g_value_get_long(a) == g_value_get_long(b);
	else if (G_VALUE_HOLDS_INT64(a))
		return g_value_get_int64(a) == g_value_get_int64(b);
	else {
		g_assert_not_reached();
		return FALSE;
	}
}

/**
 * lu_value_init_set_id:
 * @value: #GValue
 * @id: User or group ID.
 *
 * Initializes a zero-filled (uninitialized) @value with an unspecified type and
 * sets it to @id.
 */
void
lu_value_init_set_id(GValue *value, id_t id)
{
	/* Don't unnecessarily change behavior when long is enough. Only when
	   long isn't enough, we fail in more interesting ways instead of
	   silently corrupting data.

	   The (intmax_t) casts are needed to handle the (Linux) case when id_t
	   is "unsigned long', otherwise the comparison would be
	   (unsigned long)(long)id == id, which is always true. */
	if ((intmax_t)(long)id == (intmax_t)id) {
		g_value_init(value, G_TYPE_LONG);
		g_value_set_long(value, id);
	} else {
		/* FIXME: check that int64 is enough */
		g_value_init(value, G_TYPE_INT64);
		g_value_set_int64(value, id);
	}
}

/**
 * lu_value_get_id:
 * @value: #GValue
 *
 * Get the contents of @value. @value should be initialized by
 * lu_value_init_set_id() or use %G_TYPE_LONG or %G_TYPE_STRING.
 *
 * If @value does not contain a valid #id_t value, %LU_VALUE_INVALID_ID
 * is returned.
 *
 * Returns: ID value or %LU_VALUE_INVALID_ID
 */
id_t
lu_value_get_id(const GValue *value)
{
	long long val;

	if (G_VALUE_HOLDS_LONG(value))
		val = g_value_get_long(value);
	else if (G_VALUE_HOLDS_INT64(value))
		val = g_value_get_int64(value);
	else if (G_VALUE_HOLDS_STRING(value)) {
		const char *src;
		char *end;

		src = g_value_get_string(value);
		errno = 0;
		val = strtoll(src, &end, 10);
		if (errno != 0 || *end != 0 || end == src)
			g_return_val_if_reached(LU_VALUE_INVALID_ID);
	} else
		g_return_val_if_reached(LU_VALUE_INVALID_ID);
	g_return_val_if_fail((id_t)val == val, LU_VALUE_INVALID_ID);
	g_return_val_if_fail(val != LU_VALUE_INVALID_ID, LU_VALUE_INVALID_ID);
	return val;
}

/* Check whether NAME is within LIST, which is a NUL-separated sequence of
   strings, terminated by double NUL. */
static gboolean
attr_in_list(const char *attr, const char *list)
{
	size_t attr_len;

	attr_len = strlen(attr);
	while (*list != '\0') {
		size_t s_len;

		s_len = strlen(list);
		if (attr_len == s_len && strcmp(attr, list) == 0)
			return TRUE;
		list += s_len + 1;
	}
	return FALSE;
}

/**
 * lu_value_init_set_attr_from_string:
 * @value: #GValue
 * @attr: Attribute name
 * @string: The string to convert
 * @error: Filled with a #lu_error if an error occurs, or %NULL if @attr is
 * unknown
 *
 * Initializes a zero-filled (uninitialized) @value for storing a value of
 * attribute @attr and sets it to the contents of @string.  To see whether a
 * specific type is used for an attribute, see the documentation of that
 * attribute.
 *
 * The error messages returned from this function don't contain the input
 * string, to allow the caller to output at least partially usable error
 * message without disclosing the invalid string in
 * e.g. <filename>/etc/shadow</filename>, which might be somebody's misplaced
 * password.
 *
 * Returns: %TRUE on success, %FALSE on error or if @attr is unknown
 */
gboolean
lu_value_init_set_attr_from_string(GValue *value, const char *attr,
				   const char *string, struct lu_error **error)
{
	LU_ERROR_CHECK(error);
#define A(NAME) NAME "\0"
	if (attr_in_list(attr, A(LU_USERNAME) A(LU_USERPASSWORD) A(LU_GECOS)
			 A(LU_HOMEDIRECTORY) A(LU_LOGINSHELL) A(LU_GROUPNAME)
			 A(LU_GROUPPASSWORD) A(LU_MEMBERNAME)
			 A(LU_ADMINISTRATORNAME) A(LU_SHADOWNAME)
			 A(LU_SHADOWPASSWORD) A(LU_COMMONNAME) A(LU_GIVENNAME)
			 A(LU_SN) A(LU_ROOMNUMBER) A(LU_TELEPHONENUMBER)
			 A(LU_HOMEPHONE) A(LU_EMAIL))) {
		g_value_init(value, G_TYPE_STRING);
		g_value_set_string(value, string);
	} else if (attr_in_list(attr, A(LU_SHADOWLASTCHANGE) A(LU_SHADOWMIN)
				A(LU_SHADOWMAX) A(LU_SHADOWWARNING)
				A(LU_SHADOWINACTIVE) A(LU_SHADOWEXPIRE)
				A(LU_SHADOWFLAG))) {
		long l;
		char *p;

		errno = 0;
		l = strtol(string, &p, 10);
		if (errno != 0 || *p != 0 || p == string) {
			lu_error_new(error, lu_error_invalid_attribute_value,
				     _("invalid number"));
			return FALSE;
		}
		g_value_init(value, G_TYPE_LONG);
		g_value_set_long(value, l);
	} else if (attr_in_list(attr, A(LU_UIDNUMBER) A(LU_GIDNUMBER))) {
		intmax_t imax;
		char *p;

		errno = 0;
		imax = strtoimax(string, &p, 10);
		if (errno != 0 || *p != 0 || p == string
		    || (id_t)imax != imax || imax == LU_VALUE_INVALID_ID) {
			lu_error_new(error, lu_error_invalid_attribute_value,
				     _("invalid ID"));
			return FALSE;
		}
		lu_value_init_set_id(value, imax);
	} else {
		*error = NULL;
		return FALSE;
	}
#undef A
	return TRUE;
}

/**
 * lu_set_prompter:
 * @context: A context
 * @prompter: A new function to user for getting information from the user
 * @callback_data: Data for @prompter
 *
 * Changes the prompter function in a context
 */
void
lu_set_prompter(struct lu_context *context, lu_prompt_fn * prompter,
		gpointer prompter_data)
{
	g_assert(prompter != NULL);
	context->prompter = prompter;
	context->prompter_data = prompter_data;
}

/**
 * lu_get_prompter:
 * @context: A context
 * @prompter: If not %NULL, points to a place where the current prompter
 * function will be stored
 * @callback_data: If not %NULL, points to a place where the current prompter
 * function data will be stored
 *
 * Gets current prompter function from a context.
 */
void
lu_get_prompter(struct lu_context *context, lu_prompt_fn **prompter,
		gpointer *prompter_data)
{
	if (prompter != NULL) {
		*prompter = context->prompter;
	}
	if (prompter_data != NULL) {
		*prompter_data = context->prompter_data;
	}
}

/**
 * lu_set_modules:
 * @context: A context
 * @list: A list of modules (separated by whitespace or commas)
 * @error: Filled with a #lu_error if an error occurs
 *
 * Replaces the current set of modules for queries in @context to @list.
 *
 * Returns: %TRUE on success, %FALSE on failure
 */
gboolean
lu_set_modules(struct lu_context * context, const char *list,
	       struct lu_error ** error)
{
	return lu_modules_load(context, list, &context->module_names, error);
}

/**
 * lu_get_modules:
 * @context: A context
 *
 * Returns a list of modules for queries in @context.
 *
 * Returns: A list of modules separated by spaces, or %NULL if the list of
 * modules is empty.  The list should not be freed by the caller.
 */
const char *
lu_get_modules(struct lu_context *context)
{
	char *tmp = NULL, *ret = NULL;
	size_t i;

	for (i = 0; i < context->module_names->n_values; i++) {
		GValue *value;

		value = g_value_array_get_nth(context->module_names, i);
		if (tmp) {
			char *p;
			p = g_strconcat(tmp, " ",
					g_value_get_string(value), NULL);
			g_free(tmp);
			tmp = p;
		} else {
			tmp = g_value_dup_string(value);
		}
	}

	if (tmp) {
		ret = context->scache->cache(context->scache, tmp);
		g_free(tmp);
	}

	return ret;
}