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