summaryrefslogtreecommitdiffstats
path: root/login-utils/islocal.c
blob: 7c4e775c1ac9fa43c7afbc2057f2e4d7e363b708 (plain)
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
/*
 * islocal.c - returns true if user is registered in the local
 * /etc/passwd file. Written by Álvaro Martínez Echevarria,
 * alvaro@enano.etsit.upm.es, to allow peaceful coexistence with yp. Nov 94.
 *
 * Hacked a bit by poe@daimi.aau.dk
 * See also ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil*
 *
 * Hacked by Peter Breitenlohner, peb@mppmu.mpg.de,
 *   to distinguish user names where one is a prefix of the other,
 *   and to use "pathnames.h". Oct 5, 96.
 *
 * 1999-02-22 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
 * - added Native Language Support
 *
 * 2008-04-06 James Youngman, jay@gnu.org
 * - Completely rewritten to remove assumption that /etc/passwd
 *   lines are < 1024 characters long.  Also added unit tests.
 */

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

#include "closestream.h"
#include "islocal.h"
#include "nls.h"
#include "pathnames.h"

static int is_local_in_file(const char *user, const char *filename)
{
	int local = 0;
	size_t match;
	int chin, skip;
	FILE *f;

	if (NULL == (f = fopen(filename, "r")))
		return -1;

	match = 0u;
	skip = 0;
	while ((chin = getc(f)) != EOF) {
		if (skip) {
			/* Looking for the start of the next line. */
			if ('\n' == chin) {
				/* Start matching username at the next char. */
				skip = 0;
				match = 0u;
			}
		} else {
			if (':' == chin) {
				if (0 == user[match]) {
					/* Success. */
					local = 1;
					/* next line has no test coverage,
					 * but it is just an optimisation
					 * anyway.  */
					break;
				} else {
					/* we read a whole username, but it
					 * is the wrong user.  Skip to the
					 * next line.  */
					skip = 1;
				}
			} else if ('\n' == chin) {
				/* This line contains no colon; it's
				 * malformed.  No skip since we are already
				 * at the start of the next line.  */
				match = 0u;
			} else if (chin != user[match]) {
				/* username does not match. */
				skip = 1;
			} else {
				++match;
			}
		}
	}
	fclose(f);
	return local;
}

int is_local(const char *user)
{
	int rv;

	if ((rv = is_local_in_file(user, _PATH_PASSWD)) < 0)
		err(EXIT_FAILURE, _("cannot open %s"), _PATH_PASSWD);
	return rv;
}

#ifdef TEST_PROGRAM
int main(int argc, char *argv[])
{
	atexit(close_stdout);
	if (argc <= 2) {
		fprintf(stderr, _("Usage: %s <passwordfile> <username>...\n"),
			argv[0]);
		return 1;
	} else {
		int i;
		for (i = 2; i < argc; i++) {
			const int rv = is_local_in_file(argv[i], argv[1]);
			if (rv < 0) {
				perror(argv[1]);
				return 2;
			}
			printf("%d:%s\n", rv, argv[i]);
		}
		return 0;
	}
}
#endif