]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/logindefs.c
misc: consolidate version printing and close_stdout()
[thirdparty/util-linux.git] / login-utils / logindefs.c
CommitLineData
c82d9c97
KZ
1/*
2 * Copyright (C) 2003, 2004, 2005 Thorsten Kukuk
3 * Author: Thorsten Kukuk <kukuk@suse.de>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain any existing copyright
10 * notice, and this entire permission notice in its entirety,
11 * including the disclaimer of warranties.
12 *
13 * 2. Redistributions in binary form must reproduce all prior and current
14 * copyright notices, this list of conditions, and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * 3. The name of any author may not be used to endorse or promote
19 * products derived from this software without their specific prior
20 * written permission.
21 */
3f9c237d 22#include <assert.h>
c82d9c97 23#include <ctype.h>
3f9c237d
SK
24#include <errno.h>
25#include <limits.h>
c82d9c97
KZ
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
c82d9c97 29#include <sys/syslog.h>
78dd7450
OO
30#include <sys/stat.h>
31#include <sys/types.h>
32#include <pwd.h>
c82d9c97
KZ
33
34#include "c.h"
439cdf1e 35#include "closestream.h"
3f9c237d 36#include "logindefs.h"
c82d9c97 37#include "nls.h"
c82d9c97 38#include "pathnames.h"
3f9c237d 39#include "xalloc.h"
c82d9c97
KZ
40
41struct item {
42 char *name; /* name of the option. */
43 char *value; /* value of the option. */
44 char *path; /* name of config file for this option. */
45
46 struct item *next; /* pointer to next option. */
47};
48
49static struct item *list = NULL;
50
832f5cd5
KZ
51static void (*logindefs_loader)(void *) = NULL;
52static void *logindefs_loader_data = NULL;
9c44ac50 53
c82d9c97
KZ
54void free_getlogindefs_data(void)
55{
56 struct item *ptr;
57
58 ptr = list;
59 while (ptr) {
60 struct item *tmp = ptr->next;
61
62 free(ptr->path);
63 free(ptr->name);
64 free(ptr->value);
65 free(ptr);
66 ptr = tmp;
67 }
68
69 list = NULL;
70}
71
72static void store(const char *name, const char *value, const char *path)
73{
74 struct item *new = xmalloc(sizeof(struct item));
75
76 if (!name)
77 abort();
78
79 new->name = xstrdup(name);
80 new->value = value && *value ? xstrdup(value) : NULL;
81 new->path = xstrdup(path);
82 new->next = list;
83 list = new;
84}
85
9c44ac50 86void logindefs_load_file(const char *filename)
c82d9c97
KZ
87{
88 FILE *f;
89 char buf[BUFSIZ];
90
91 f = fopen(filename, "r");
92 if (!f)
93 return;
94
95 while (fgets(buf, sizeof(buf), f)) {
96
97 char *p, *name, *data = NULL;
98
99 if (*buf == '#' || *buf == '\n')
3f9c237d 100 continue; /* only comment or empty line */
c82d9c97
KZ
101
102 p = strchr(buf, '#');
103 if (p)
104 *p = '\0';
105 else {
106 size_t n = strlen(buf);
107 if (n && *(buf + n - 1) == '\n')
108 *(buf + n - 1) = '\0';
109 }
110
111 if (!*buf)
3f9c237d 112 continue; /* empty line */
c82d9c97
KZ
113
114 /* ignore space at begin of the line */
115 name = buf;
3f9c237d 116 while (*name && isspace((unsigned)*name))
c82d9c97
KZ
117 name++;
118
119 /* go to the end of the name */
120 data = name;
3f9c237d 121 while (*data && !(isspace((unsigned)*data) || *data == '='))
c82d9c97
KZ
122 data++;
123 if (data > name && *data)
124 *data++ = '\0';
125
126 if (!*name || data == name)
127 continue;
128
129 /* go to the begin of the value */
3f9c237d
SK
130 while (*data
131 && (isspace((unsigned)*data) || *data == '='
132 || *data == '"'))
133 data++;
c82d9c97
KZ
134
135 /* remove space at the end of the value */
136 p = data + strlen(data);
137 if (p > data)
138 p--;
3f9c237d 139 while (p > data && (isspace((unsigned)*p) || *p == '"'))
c82d9c97
KZ
140 *p-- = '\0';
141
142 store(name, data, filename);
143 }
144
145 fclose(f);
146}
147
832f5cd5
KZ
148void logindefs_set_loader(void (*loader)(void *data), void *data)
149{
150 logindefs_loader = loader;
151 logindefs_loader_data = data;
152}
153
4082ab2c 154static void load_defaults(void)
9c44ac50 155{
832f5cd5
KZ
156 if (logindefs_loader)
157 logindefs_loader(logindefs_loader_data);
9c44ac50
LN
158 else
159 logindefs_load_file(_PATH_LOGINDEFS);
160}
161
c82d9c97
KZ
162static struct item *search(const char *name)
163{
164 struct item *ptr;
165
166 if (!list)
9c44ac50 167 load_defaults();
c82d9c97
KZ
168
169 ptr = list;
170 while (ptr != NULL) {
171 if (strcasecmp(name, ptr->name) == 0)
172 return ptr;
173 ptr = ptr->next;
174 }
175
176 return NULL;
177}
178
179static const char *search_config(const char *name)
180{
181 struct item *ptr;
182
183 ptr = list;
184 while (ptr != NULL) {
185 if (strcasecmp(name, ptr->name) == 0)
186 return ptr->path;
187 ptr = ptr->next;
188 }
189
190 return NULL;
191}
192
193int getlogindefs_bool(const char *name, int dflt)
194{
3f9c237d 195 struct item *ptr = search(name);
c82d9c97
KZ
196 return ptr && ptr->value ? (strcasecmp(ptr->value, "yes") == 0) : dflt;
197}
198
62342745 199unsigned long getlogindefs_num(const char *name, unsigned long dflt)
c82d9c97
KZ
200{
201 struct item *ptr = search(name);
202 char *end = NULL;
c9baf5da 203 unsigned long retval;
c82d9c97
KZ
204
205 if (!ptr || !ptr->value)
206 return dflt;
207
208 errno = 0;
c9baf5da 209 retval = strtoul(ptr->value, &end, 0);
c82d9c97
KZ
210 if (end && *end == '\0' && !errno)
211 return retval;
212
213 syslog(LOG_NOTICE, _("%s: %s contains invalid numerical value: %s"),
3f9c237d 214 search_config(name), name, ptr->value);
c82d9c97
KZ
215 return dflt;
216}
217
218/*
219 * Returns:
220 * @dflt if @name not found
221 * "" (empty string) if found, but value not defined
222 * "string" if found
223 */
224const char *getlogindefs_str(const char *name, const char *dflt)
225{
226 struct item *ptr = search(name);
227
228 if (!ptr)
229 return dflt;
230 if (!ptr->value)
231 return "";
232 return ptr->value;
233}
234
607e6b7c 235/*
a8077509 236 * For compatibility with shadow-utils we have to support additional
607e6b7c
KZ
237 * syntax for environment variables in login.defs(5) file. The standard
238 * syntax is:
239 *
240 * ENV_FOO data
241 *
242 * but shadow-utils supports also
243 *
244 * ENV_FOO FOO=data
245 *
246 * the FOO= prefix has to be remove before we call setenv().
247 */
248int logindefs_setenv(const char *name, const char *conf, const char *dflt)
249{
250 const char *val = getlogindefs_str(conf, dflt);
251 const char *p;
252
253 if (!val)
254 return -1;
255
256 p = strchr(val, '=');
257 if (p) {
258 size_t sz = strlen(name);
259
260 if (strncmp(val, name, sz) == 0 && *(p + 1)) {
261 val = p + 1;
262 if (*val == '"')
263 val++;
264 if (!*val)
265 val = dflt;
266 }
267 }
268
269 return val ? setenv(name, val, 1) : -1;
270}
271
78dd7450
OO
272/*
273 * We need to check the effective UID/GID. For example, $HOME could be on a
274 * root-squashed NFS or on an NFS with UID mapping, and access(2) uses the
275 * real UID/GID. Then open(2) seems as the surest solution.
276 * -- kzak@redhat.com (10-Apr-2009)
277 */
278int effective_access(const char *path, int mode)
279{
280 int fd = open(path, mode);
281 if (fd != -1)
282 close(fd);
283 return fd == -1 ? -1 : 0;
284}
285
286
287/*
288 * Check the per-account or the global hush-login setting.
289 *
290 * Hushed mode is enabled:
291 *
292 * a) if a global (e.g. /etc/hushlogins) hush file exists:
293 * 1) for ALL ACCOUNTS if the file is empty
294 * 2) for the current user if the username or shell is found in the file
295 *
296 * b) if a ~/.hushlogin file exists
297 *
298 * The ~/.hushlogin file is ignored if the global hush file exists.
299 *
300 * The HUSHLOGIN_FILE login.def variable overrides the default hush filename.
301 *
302 * Note that shadow-utils login(1) does not support "a1)". The "a1)" is
303 * necessary if you want to use PAM for "Last login" message.
304 *
305 * -- Karel Zak <kzak@redhat.com> (26-Aug-2011)
306 *
307 *
308 * The per-account check requires some explanation: As root we may not be able
309 * to read the directory of the user if it is on an NFS-mounted filesystem. We
310 * temporarily set our effective uid to the user-uid, making sure that we keep
311 * root privileges in the real uid.
312 *
313 * A portable solution would require a fork(), but we rely on Linux having the
314 * BSD setreuid().
315 */
316
29cc2a55 317int get_hushlogin_status(struct passwd *pwd, int force_check)
78dd7450
OO
318{
319 const char *files[] = { _PATH_HUSHLOGINS, _PATH_HUSHLOGIN, NULL };
320 const char *file;
321 char buf[BUFSIZ];
322 int i;
323
324 file = getlogindefs_str("HUSHLOGIN_FILE", NULL);
325 if (file) {
326 if (!*file)
327 return 0; /* empty HUSHLOGIN_FILE defined */
328
329 files[0] = file;
330 files[1] = NULL;
331 }
332
333 for (i = 0; files[i]; i++) {
334 int ok = 0;
335
336 file = files[i];
337
338 /* global hush-file */
339 if (*file == '/') {
340 struct stat st;
341 FILE *f;
342
343 if (stat(file, &st) != 0)
344 continue; /* file does not exist */
345
346 if (st.st_size == 0)
347 return 1; /* for all accounts */
348
349 f = fopen(file, "r");
350 if (!f)
351 continue; /* ignore errors... */
352
353 while (ok == 0 && fgets(buf, sizeof(buf), f)) {
b0f97de5
TS
354 if (buf[0] != '\0')
355 buf[strlen(buf) - 1] = '\0';
78dd7450
OO
356 ok = !strcmp(buf, *buf == '/' ? pwd->pw_shell :
357 pwd->pw_name);
358 }
359 fclose(f);
360 if (ok)
361 return 1; /* found username/shell */
362
363 return 0; /* ignore per-account files */
364 }
365
366 /* per-account setting */
367 if (strlen(pwd->pw_dir) + sizeof(file) + 2 > sizeof(buf))
368 continue;
29cc2a55
OO
369
370 sprintf(buf, "%s/%s", pwd->pw_dir, file);
371
372 if (force_check) {
78dd7450
OO
373 uid_t ruid = getuid();
374 gid_t egid = getegid();
375
78dd7450
OO
376 if (setregid(-1, pwd->pw_gid) == 0 &&
377 setreuid(0, pwd->pw_uid) == 0)
378 ok = effective_access(buf, O_RDONLY) == 0;
379
380 if (setuid(0) != 0 ||
381 setreuid(ruid, 0) != 0 ||
382 setregid(-1, egid) != 0) {
383 syslog(LOG_ALERT, _("hush login status: restore original IDs failed"));
384 exit(EXIT_FAILURE);
385 }
386 if (ok)
387 return 1; /* enabled by user */
388 }
29cc2a55
OO
389 else {
390 int rc;
391 rc = effective_access(buf, O_RDONLY);
392 if (rc == 0)
393 return 1;
394 else if (rc == -1 && errno == EACCES)
395 return -1;
396 }
397
78dd7450
OO
398 }
399
400 return 0;
401}
c82d9c97
KZ
402#ifdef TEST_PROGRAM
403int main(int argc, char *argv[])
404{
405 char *name, *type;
2c308875 406 close_stdout_atexit();
c82d9c97
KZ
407
408 if (argc <= 1)
409 errx(EXIT_FAILURE, "usage: %s <filename> "
3f9c237d 410 "[<str|num|bool> <valname>]", argv[0]);
c82d9c97 411
9c44ac50 412 logindefs_load_file(argv[1]);
c82d9c97 413
3f9c237d 414 if (argc != 4) { /* list all */
c82d9c97
KZ
415 struct item *ptr;
416
417 for (ptr = list; ptr; ptr = ptr->next)
3f9c237d
SK
418 printf("%s: $%s: '%s'\n", ptr->path, ptr->name,
419 ptr->value);
c82d9c97
KZ
420
421 return EXIT_SUCCESS;
422 }
423
424 type = argv[2];
425 name = argv[3];
426
427 if (strcmp(type, "str") == 0)
428 printf("$%s: '%s'\n", name, getlogindefs_str(name, "DEFAULT"));
429 else if (strcmp(type, "num") == 0)
430 printf("$%s: '%ld'\n", name, getlogindefs_num(name, 0));
431 else if (strcmp(type, "bool") == 0)
3f9c237d
SK
432 printf("$%s: '%s'\n", name,
433 getlogindefs_bool(name, 0) ? "Y" : "N");
c82d9c97
KZ
434
435 return EXIT_SUCCESS;
436}
437#endif