]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/logindefs.c
2 * Copyright (C) 2003, 2004, 2005 Thorsten Kukuk
3 * Author: Thorsten Kukuk <kukuk@suse.de>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
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
29 #include <sys/syslog.h>
31 #include <sys/types.h>
35 #include "closestream.h"
36 #include "logindefs.h"
38 #include "pathnames.h"
42 static void (*logindefs_loader
)(void *) = NULL
;
43 static void *logindefs_loader_data
= NULL
;
45 void logindefs_set_loader(void (*loader
)(void *data
), void *data
)
47 logindefs_loader
= loader
;
48 logindefs_loader_data
= data
;
54 char *name
; /* name of the option. */
55 char *value
; /* value of the option. */
56 char *path
; /* name of config file for this option. */
58 struct item
*next
; /* pointer to next option. */
61 static struct item
*list
= NULL
;
63 void free_getlogindefs_data(void)
69 struct item
*tmp
= ptr
->next
;
81 static void store(const char *name
, const char *value
, const char *path
)
83 struct item
*new = xmalloc(sizeof(struct item
));
88 new->name
= xstrdup(name
);
89 new->value
= value
&& *value
? xstrdup(value
) : NULL
;
90 new->path
= xstrdup(path
);
95 void logindefs_load_file(const char *filename
)
100 f
= fopen(filename
, "r");
104 while (fgets(buf
, sizeof(buf
), f
)) {
106 char *p
, *name
, *data
= NULL
;
108 if (*buf
== '#' || *buf
== '\n')
109 continue; /* only comment or empty line */
111 p
= strchr(buf
, '#');
115 size_t n
= strlen(buf
);
116 if (n
&& *(buf
+ n
- 1) == '\n')
117 *(buf
+ n
- 1) = '\0';
121 continue; /* empty line */
123 /* ignore space at begin of the line */
125 while (*name
&& isspace((unsigned)*name
))
128 /* go to the end of the name */
130 while (*data
&& !(isspace((unsigned)*data
) || *data
== '='))
132 if (data
> name
&& *data
)
135 if (!*name
|| data
== name
)
138 /* go to the begin of the value */
140 && (isspace((unsigned)*data
) || *data
== '='
144 /* remove space at the end of the value */
145 p
= data
+ strlen(data
);
148 while (p
> data
&& (isspace((unsigned)*p
) || *p
== '"'))
151 store(name
, data
, filename
);
157 static void load_defaults(void)
159 if (logindefs_loader
)
160 logindefs_loader(logindefs_loader_data
);
162 logindefs_load_file(_PATH_LOGINDEFS
);
165 static struct item
*search(const char *name
)
173 while (ptr
!= NULL
) {
174 if (strcasecmp(name
, ptr
->name
) == 0)
182 static const char *search_config(const char *name
)
187 while (ptr
!= NULL
) {
188 if (strcasecmp(name
, ptr
->name
) == 0)
196 int getlogindefs_bool(const char *name
, int dflt
)
198 struct item
*ptr
= search(name
);
199 return ptr
&& ptr
->value
? (strcasecmp(ptr
->value
, "yes") == 0) : dflt
;
202 unsigned long getlogindefs_num(const char *name
, unsigned long dflt
)
204 struct item
*ptr
= search(name
);
206 unsigned long retval
;
208 if (!ptr
|| !ptr
->value
)
212 retval
= strtoul(ptr
->value
, &end
, 0);
213 if (end
&& *end
== '\0' && !errno
)
216 syslog(LOG_NOTICE
, _("%s: %s contains invalid numerical value: %s"),
217 search_config(name
), name
, ptr
->value
);
223 * @dflt if @name not found
224 * "" (empty string) if found, but value not defined
227 const char *getlogindefs_str(const char *name
, const char *dflt
)
229 struct item
*ptr
= search(name
);
238 #else /* !HAVE_LIBECONF */
240 #include <libeconf.h>
242 static econf_file
*file
= NULL
;
244 void free_getlogindefs_data(void)
250 static void load_defaults(void)
255 free_getlogindefs_data();
257 error
= econf_readDirs(&file
,
263 "/etc", "login", "defs", "= \t", "#");
266 syslog(LOG_NOTICE
, _("Error reading login.defs: %s"),
267 econf_errString(error
));
269 if (logindefs_loader
)
270 logindefs_loader(logindefs_loader_data
);
274 void logindefs_load_file(const char *filename
)
276 econf_file
*file_l
= NULL
, *file_m
= NULL
;
279 logindefs_loader
= NULL
; /* No recursion */
282 xasprintf(&path
, _PATH_VENDORDIR
"/%s", filename
);
284 if (!econf_readFile(&file_l
, path
, "= \t", "#")) {
287 else if (!econf_mergeFiles(&file_m
, file
, file_l
)) {
296 xasprintf(&path
, "/etc/%s", filename
);
298 if (!econf_readFile(&file_l
, path
, "= \t", "#")) {
301 else if (!econf_mergeFiles(&file_m
, file
, file_l
)) {
307 /* Try original filename, could be relative */
308 } else if (!econf_readFile(&file_l
, filename
, "= \t", "#")) {
311 else if (!econf_mergeFiles(&file_m
, file
, file_l
)) {
320 int getlogindefs_bool(const char *name
, int dflt
)
331 if ((error
= econf_getBoolValue(file
, NULL
, name
, &value
))) {
332 if (error
!= ECONF_NOKEY
)
333 syslog(LOG_NOTICE
, _("couldn't fetch %s: %s"), name
,
334 econf_errString(error
));
340 unsigned long getlogindefs_num(const char *name
, unsigned long dflt
)
351 if ((error
= econf_getUInt64Value(file
, NULL
, name
, &value
))) {
352 if (error
!= ECONF_NOKEY
)
353 syslog(LOG_NOTICE
, _("couldn't fetch %s: %s"), name
,
354 econf_errString(error
));
362 * @dflt if @name not found
363 * "" (empty string) if found, but value not defined
366 const char *getlogindefs_str(const char *name
, const char *dflt
)
377 if ((error
= econf_getStringValue(file
, NULL
, name
, &value
))) {
378 if (error
!= ECONF_NOKEY
)
379 syslog(LOG_NOTICE
, _("couldn't fetch %s: %s"), name
,
380 econf_errString(error
));
388 #endif /* !HAVE_LIBECONF */
391 * For compatibility with shadow-utils we have to support additional
392 * syntax for environment variables in login.defs(5) file. The standard
397 * but shadow-utils supports also
401 * the FOO= prefix has to be remove before we call setenv().
403 int logindefs_setenv(const char *name
, const char *conf
, const char *dflt
)
405 const char *val
= getlogindefs_str(conf
, dflt
);
411 p
= strchr(val
, '=');
413 size_t sz
= strlen(name
);
415 if (strncmp(val
, name
, sz
) == 0 && *(p
+ 1)) {
424 return val
? setenv(name
, val
, 1) : -1;
428 * We need to check the effective UID/GID. For example, $HOME could be on a
429 * root-squashed NFS or on an NFS with UID mapping, and access(2) uses the
430 * real UID/GID. Then open(2) seems as the surest solution.
431 * -- kzak@redhat.com (10-Apr-2009)
433 int effective_access(const char *path
, int mode
)
435 int fd
= open(path
, mode
);
438 return fd
== -1 ? -1 : 0;
442 * Check the per-account or the global hush-login setting.
444 * Hushed mode is enabled:
446 * a) if a global (e.g. /etc/hushlogins) hush file exists:
447 * 1) for ALL ACCOUNTS if the file is empty
448 * 2) for the current user if the username or shell is found in the file
450 * b) if a ~/.hushlogin file exists
452 * The ~/.hushlogin file is ignored if the global hush file exists.
454 * The HUSHLOGIN_FILE login.def variable overrides the default hush filename.
456 * Note that shadow-utils login(1) does not support "a1)". The "a1)" is
457 * necessary if you want to use PAM for "Last login" message.
459 * -- Karel Zak <kzak@redhat.com> (26-Aug-2011)
462 * The per-account check requires some explanation: As root we may not be able
463 * to read the directory of the user if it is on an NFS-mounted filesystem. We
464 * temporarily set our effective uid to the user-uid, making sure that we keep
465 * root privileges in the real uid.
467 * A portable solution would require a fork(), but we rely on Linux having the
471 int get_hushlogin_status(struct passwd
*pwd
, int force_check
)
473 const char *files
[] = { _PATH_HUSHLOGINS
, _PATH_HUSHLOGIN
, NULL
};
478 file
= getlogindefs_str("HUSHLOGIN_FILE", NULL
);
481 return 0; /* empty HUSHLOGIN_FILE defined */
487 for (i
= 0; files
[i
]; i
++) {
492 /* global hush-file */
497 if (stat(file
, &st
) != 0)
498 continue; /* file does not exist */
501 return 1; /* for all accounts */
503 f
= fopen(file
, "r");
505 continue; /* ignore errors... */
507 while (ok
== 0 && fgets(buf
, sizeof(buf
), f
)) {
509 buf
[strlen(buf
) - 1] = '\0';
510 ok
= !strcmp(buf
, *buf
== '/' ? pwd
->pw_shell
:
515 return 1; /* found username/shell */
517 return 0; /* ignore per-account files */
520 /* per-account setting */
521 if (strlen(pwd
->pw_dir
) + strlen(file
) + 2 > sizeof(buf
))
524 if (snprintf(buf
, sizeof(buf
), "%s/%s", pwd
->pw_dir
, file
) < 0)
528 uid_t ruid
= getuid();
529 gid_t egid
= getegid();
531 if (setregid(-1, pwd
->pw_gid
) == 0 &&
532 setreuid(0, pwd
->pw_uid
) == 0)
533 ok
= effective_access(buf
, O_RDONLY
) == 0;
535 if (setuid(0) != 0 ||
536 setreuid(ruid
, 0) != 0 ||
537 setregid(-1, egid
) != 0) {
538 syslog(LOG_ALERT
, _("hush login status: restore original IDs failed"));
542 return 1; /* enabled by user */
546 rc
= effective_access(buf
, O_RDONLY
);
550 if (rc
== -1 && errno
== EACCES
)
559 int main(int argc
, char *argv
[])
562 close_stdout_atexit();
565 errx(EXIT_FAILURE
, "usage: %s <filename> "
566 "[<str|num|bool> <valname>]", argv
[0]);
568 logindefs_load_file(argv
[1]);
570 if (argc
!= 4) { /* list all */
573 char *keys
[] = {"END", "EMPTY", "CRAZY3", "CRAZY2", "CRAZY1",
574 "BOOLEAN", "NUMBER", "STRING", "HELLO_WORLD",
577 for (i
= 0; keys
[i
] != NULL
; i
++) {
580 econf_getStringValue(file
, NULL
, keys
[i
], &value
);
581 printf ("%s: $%s: '%s'\n", argv
[1], keys
[i
], value
);
589 for (ptr
= list
; ptr
; ptr
= ptr
->next
)
590 printf("%s: $%s: '%s'\n", ptr
->path
, ptr
->name
,
599 if (strcmp(type
, "str") == 0)
600 printf("$%s: '%s'\n", name
, getlogindefs_str(name
, "DEFAULT"));
601 else if (strcmp(type
, "num") == 0)
602 printf("$%s: '%ld'\n", name
, getlogindefs_num(name
, 0));
603 else if (strcmp(type
, "bool") == 0)
604 printf("$%s: '%s'\n", name
,
605 getlogindefs_bool(name
, 0) ? "Y" : "N");