1 /* This file is part of pam-modules.
2    Copyright (C) 2008, 2010-2012, 2014-2015, 2018 Sergey Poznyakoff
3 
4    This program is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by the
6    Free Software Foundation; either version 3 of the License, or (at your
7    option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16 
17 #include <graypam.h>
18 
19 static struct pam_opt *
20 find_opt(struct pam_opt *opt, const char *str, const char **value)
21 {
22 	size_t len = strlen(str);
23 	int isbool;
24 
25 	if (len > 2 && memcmp(str, "no", 2) == 0) {
26 		*value = NULL;
27 		str += 2;
28 		isbool = 1;
29 	} else {
30 		isbool = 0;
31 		*value = str;
32 	}
33 
34 	for (; opt->name; opt++) {
35 		if (len >= opt->len
36 		    && memcmp(opt->name, str, opt->len) == 0
37 		    && (!isbool || opt->type == pam_opt_bool)) {
38 			int eq = str[opt->len] == '=';
39 
40 			switch (opt->type) {
41 			case pam_opt_long:
42 			case pam_opt_string:
43 			case pam_opt_enum:
44 				if (!eq)
45 					continue;
46 				*value = str + opt->len + 1;
47 				break;
48 
49 			case pam_opt_null:
50 				if (eq)
51 					*value = str + opt->len + 1;
52 				else
53 					*value = NULL;
54 				break;
55 
56 			default:
57 				if (eq)
58 					continue;
59 				break;
60 			}
61 			return opt;
62 		}
63 	}
64 	return NULL;
65 }
66 
67 int
68 find_value(const char **enumstr, const char *value)
69 {
70 	int i;
71 	for (i = 0; *enumstr; enumstr++, i++)
72 		if (strcmp(*enumstr, value) == 0)
73 			return i;
74 	return -1;
75 }
76 
77 int
78 gray_parseopt(struct pam_opt *opt, int argc, const char **argv)
79 {
80 	long n;
81 	char *s;
82 	int i;
83 	int rc = 0;
84 
85 	for (i = 0; i < argc; ++i, ++argv) {
86 		const char *value;
87 		struct pam_opt *p = find_opt(opt, *argv, &value);
88 
89 		if (!p) {
90 			_pam_log(LOG_ERR,
91 				 "%s: unknown option", *argv);
92 			rc = 1;
93 			continue;
94 		}
95 
96 		switch (p->type) {
97 		case pam_opt_long:
98 			n = strtol(value, &s, 0);
99 			if (*s) {
100 				_pam_log(LOG_ERR,
101 					 "%s: %s is not a valid number",
102 					 p->name, value);
103 				rc = 1;
104 				continue;
105 			}
106 			*(long*)p->data = n;
107 			break;
108 
109 		case pam_opt_const:
110 			*(long*)p->data = p->v.value;
111 			break;
112 
113 		case pam_opt_string:
114 			*(const char**)p->data = value;
115 			break;
116 
117 		case pam_opt_bool:
118 			if (p->v.value) {
119 				if (value)
120 					*(int*)p->data |= p->v.value;
121 				else
122 					*(int*)p->data &= ~p->v.value;
123 			} else
124 				*(int*)p->data = value != NULL;
125 			break;
126 
127 		case pam_opt_bitmask:
128 			*(int*)p->data |= p->v.value;
129 			break;
130 
131 		case pam_opt_bitmask_rev:
132 			*(int*)p->data &= ~p->v.value;
133 			break;
134 
135 		case pam_opt_enum:
136 			n = find_value(p->v.enumstr, value);
137 			if (n == -1) {
138 				_pam_log(LOG_ERR,
139 					 "%s: invalid value %s",
140 					 p->name, value);
141 				rc = 1;
142 				continue;
143 			}
144 			*(int*)p->data = n;
145 			break;
146 
147 		case pam_opt_rest:
148 			*(int*)p->data = i + 1;
149 			return 0;
150 
151 		case pam_opt_null:
152 			break;
153 		}
154 
155 		if (p->func && p->func (p, value))
156 			rc = 1;
157 	}
158 	return rc;
159 }
160