1 /*
2  * Copyright (c) 2008,2009 by Dmitry V. Levin
3  * Copyright (c) 2021 by Solar Designer
4  * See LICENSE
5  */
6 
7 #ifdef _MSC_VER
8 #define _CRT_NONSTDC_NO_WARNINGS /* we use POSIX function names */
9 #define _CRT_SECURE_NO_WARNINGS /* we use fopen(), strerror(), sprintf() */
10 #endif
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <stdarg.h>
16 #include <errno.h>
17 #include <sys/stat.h>
18 
19 #include "passwdqc.h"
20 #include "concat.h"
21 
22 static char *mkreason(const char *what, const char *pathname,
23     unsigned int lineno, const char *why)
24 {
25 	char buf[sizeof(unsigned int) * 3 + 1];
26 	const char *at_line = (lineno ? " at line " : "");
27 	const char *at_num = (lineno ? buf : "");
28 
29 	if (lineno)
30 		sprintf(buf, "%u", lineno);
31 	return concat(what, " \"", pathname, "\"", at_line, at_num, ": ",
32 	    (why ? why : strerror(errno)), NULL);
33 }
34 
35 static int
36 parse_file(FILE *fp, passwdqc_params_t *params, char **reason,
37     const char *pathname)
38 {
39 	unsigned int lineno;
40 	char buf[8192];
41 
42 	for (lineno = 1; fgets(buf, sizeof(buf), fp); ++lineno) {
43 		char *str, *end, *rt;
44 		const char *cstr;
45 		int rc;
46 
47 		if (strlen(buf) >= sizeof(buf) - 1) {
48 			*reason = mkreason("Error reading", pathname,
49 			    lineno, "Line too long");
50 			return -1;
51 		}
52 
53 		str = buf + strspn(buf, " \t\r\n");
54 		if (!*str || *str == '#')
55 			continue;
56 
57 		if ((end = strpbrk(str, "\r\n")))
58 			*end = '\0';
59 		else
60 			end = str + strlen(str);
61 
62 		while (end > str && (*--end == ' ' || *end == '\t'))
63 			*end = '\0';
64 
65 		cstr = str;
66 		if ((rc = passwdqc_params_parse(params, &rt, 1, &cstr))) {
67 			*reason = mkreason("Error loading", pathname,
68 			    lineno, (rt ? rt : "Out of memory"));
69 			free(rt);
70 			return rc;
71 		}
72 	}
73 
74 	if (!feof(fp) || ferror(fp)) {
75 		*reason = mkreason("Error reading", pathname, 0, NULL);
76 		return -1;
77 	}
78 
79 	return 0;
80 }
81 
82 struct dev_ino_t;
83 struct dev_ino_t {
84 	struct dev_ino_t *next;
85 	dev_t dev;
86 	ino_t ino;
87 };
88 
89 static struct dev_ino_t *dev_ino_head;
90 
91 int
92 passwdqc_params_load(passwdqc_params_t *params, char **reason,
93     const char *pathname)
94 {
95 	int rc;
96 	FILE *fp;
97 	struct dev_ino_t di, *di_p;
98 	struct stat st;
99 
100 	if (!(fp = fopen(pathname, "r"))) {
101 		*reason = mkreason("Error opening", pathname, 0, NULL);
102 		return -1;
103 	}
104 
105 	if (fstat(fileno(fp), &st)) {
106 		*reason = mkreason("Error stat", pathname, 0, NULL);
107 		fclose(fp);
108 		return -1;
109 	}
110 
111 	di.dev = st.st_dev;
112 	di.ino = st.st_ino;
113 	for (di_p = dev_ino_head; di_p; di_p = di_p->next)
114 		if (di_p->dev == di.dev && di_p->ino == di.ino)
115 			break;
116 	if (di_p) {
117 		*reason = mkreason("Error opening", pathname, 0,
118 		    "Loop detected");
119 		fclose(fp);
120 		return -1;
121 	}
122 
123 	di.next = dev_ino_head;
124 	dev_ino_head = &di;
125 
126 	rc = parse_file(fp, params, reason, pathname);
127 	fclose(fp);
128 
129 	dev_ino_head = dev_ino_head->next;
130 	return rc;
131 }
132