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