]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/logindefs.c
07a977660bbbc5e85a0d9ba6bb7e7c2b85c1d75e
[thirdparty/util-linux.git] / login-utils / logindefs.c
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 */
22 #include <assert.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/syslog.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <pwd.h>
33
34 #include "c.h"
35 #include "closestream.h"
36 #include "logindefs.h"
37 #include "nls.h"
38 #include "pathnames.h"
39 #include "xalloc.h"
40
41 struct 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
49 static struct item *list = NULL;
50
51 static void (*logindefs_loader)(void *) = NULL;
52 static void *logindefs_loader_data = NULL;
53
54 void 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
72 static 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
86 void logindefs_load_file(const char *filename)
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')
100 continue; /* only comment or empty line */
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)
112 continue; /* empty line */
113
114 /* ignore space at begin of the line */
115 name = buf;
116 while (*name && isspace((unsigned)*name))
117 name++;
118
119 /* go to the end of the name */
120 data = name;
121 while (*data && !(isspace((unsigned)*data) || *data == '='))
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 */
130 while (*data
131 && (isspace((unsigned)*data) || *data == '='
132 || *data == '"'))
133 data++;
134
135 /* remove space at the end of the value */
136 p = data + strlen(data);
137 if (p > data)
138 p--;
139 while (p > data && (isspace((unsigned)*p) || *p == '"'))
140 *p-- = '\0';
141
142 store(name, data, filename);
143 }
144
145 fclose(f);
146 }
147
148 void logindefs_set_loader(void (*loader)(void *data), void *data)
149 {
150 logindefs_loader = loader;
151 logindefs_loader_data = data;
152 }
153
154 static void load_defaults(void)
155 {
156 if (logindefs_loader)
157 logindefs_loader(logindefs_loader_data);
158 else
159 logindefs_load_file(_PATH_LOGINDEFS);
160 }
161
162 static struct item *search(const char *name)
163 {
164 struct item *ptr;
165
166 if (!list)
167 load_defaults();
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
179 static 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
193 int getlogindefs_bool(const char *name, int dflt)
194 {
195 struct item *ptr = search(name);
196 return ptr && ptr->value ? (strcasecmp(ptr->value, "yes") == 0) : dflt;
197 }
198
199 unsigned long getlogindefs_num(const char *name, unsigned long dflt)
200 {
201 struct item *ptr = search(name);
202 char *end = NULL;
203 unsigned long retval;
204
205 if (!ptr || !ptr->value)
206 return dflt;
207
208 errno = 0;
209 retval = strtoul(ptr->value, &end, 0);
210 if (end && *end == '\0' && !errno)
211 return retval;
212
213 syslog(LOG_NOTICE, _("%s: %s contains invalid numerical value: %s"),
214 search_config(name), name, ptr->value);
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 */
224 const 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
235 /*
236 * For compatibility with shadow-utils we have to support additional
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 */
248 int 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
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 */
278 int 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
317 int get_hushlogin_status(struct passwd *pwd, int force_check)
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)) {
354 if (buf[0] != '\0')
355 buf[strlen(buf) - 1] = '\0';
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;
369
370 sprintf(buf, "%s/%s", pwd->pw_dir, file);
371
372 if (force_check) {
373 uid_t ruid = getuid();
374 gid_t egid = getegid();
375
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 }
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
398 }
399
400 return 0;
401 }
402 #ifdef TEST_PROGRAM
403 int main(int argc, char *argv[])
404 {
405 char *name, *type;
406 atexit(close_stdout);
407
408 if (argc <= 1)
409 errx(EXIT_FAILURE, "usage: %s <filename> "
410 "[<str|num|bool> <valname>]", argv[0]);
411
412 logindefs_load_file(argv[1]);
413
414 if (argc != 4) { /* list all */
415 struct item *ptr;
416
417 for (ptr = list; ptr; ptr = ptr->next)
418 printf("%s: $%s: '%s'\n", ptr->path, ptr->name,
419 ptr->value);
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)
432 printf("$%s: '%s'\n", name,
433 getlogindefs_bool(name, 0) ? "Y" : "N");
434
435 return EXIT_SUCCESS;
436 }
437 #endif