1 /* This file is part of pam-modules.
2    Copyright (C) 2009-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 struct keyword *
gray_find_keyword(struct keyword * kwtab,const char * str,size_t len)20 gray_find_keyword(struct keyword *kwtab, const char *str, size_t len)
21 {
22 	for (; kwtab->name; kwtab++)
23 		if (kwtab->len == len
24 		    && strncmp(kwtab->name, str, kwtab->len) == 0)
25 			return kwtab;
26 	return NULL;
27 }
28 
29 static struct keyword vartab[] = {
30 	DCL("service", PAM_SERVICE),
31 	DCL("user", PAM_USER),
32 	DCL("tty", PAM_TTY),
33 	DCL("rhost", PAM_RHOST),
34 	DCL("ruser", PAM_RUSER),
35 	DCL("prompt", PAM_USER_PROMPT),
36 	DCL("password", PAM_AUTHTOK),
37 	{ NULL }
38 };
39 
40 static int
var_tok(const char * str,const char ** pvar,size_t * plen)41 var_tok(const char *str, const char ** pvar, size_t *plen)
42 {
43 	size_t len;
44 
45 	for (len = 0; str[len]; len++) {
46 		if (str[len] == '}' || str[len] == ':') {
47 			*pvar = str;
48 			*plen = len;
49 			return 0;
50 		}
51 	}
52 	return 1;
53 }
54 
55 static int
repl_tok(const char * str,const char ** pret,size_t * plen)56 repl_tok(const char *str, const char ** pret, size_t *plen)
57 {
58 	size_t len;
59 
60 	for (len = 0; str[len]; len++) {
61 		if (str[len] == '}') {
62 			*pret = str;
63 			*plen = len;
64 			return 0;
65 		}
66 	}
67 	return 1;
68 }
69 
70 #define ISKW(c) ((c) && (isalnum(c) || (c) == '_'))
71 
72 static int
get_variable(pam_handle_t * pamh,const char * str,gray_slist_t slist,const char ** endp)73 get_variable(pam_handle_t *pamh, const char *str, gray_slist_t slist,
74 	     const char **endp)
75 {
76 	const char *name;
77 	size_t namelen;
78 	const char *repl = NULL;
79 	size_t repllen = 0;
80 	const char *val;
81 	size_t vallen;
82 	struct keyword *kw;
83 	const char *end;
84 	int rc;
85 
86 	str++; /* Get past the initial $ */
87 	if (*str == '{') {
88 		str++;
89 
90 		if (var_tok(str, &name, &namelen))
91 			return 1;
92 
93 		end = str + namelen;
94 		if (*end == ':') {
95 			end++;
96 			if (*end == '-')
97 				end++;
98 			if (repl_tok(end, &repl, &repllen))
99 				return 1;
100 			end += repllen;
101 		}
102 		end++;
103 	} else {
104 		name = end = str;
105 		for (namelen = 0; ISKW(*end); namelen++, end++)
106 			;
107 	}
108 
109 	kw = gray_find_keyword(vartab, name, namelen);
110 	if (!kw) {
111 		_pam_log(LOG_ERR,
112 			 "unknown PAM variable: %*.*s",
113 			 namelen, namelen, name);
114 		return 1;
115 	}
116 
117 	rc = pam_get_item(pamh, kw->code, (const void**) &val);
118 	if (rc) {
119 		_pam_log(LOG_ERR,
120 			 "cannot obtain variable %s: %s",
121 			 kw->name, pam_strerror(pamh, rc));
122 		return 1;
123 	}
124 
125 	if (!val) {
126 		if (repl) {
127 			val = repl;
128 			vallen = repllen;
129 		} else {
130 			val = "";
131 			vallen = 0;
132 		}
133 	} else
134 		vallen = strlen(val);
135 
136 	gray_escape_string(slist, val, vallen);
137 	*endp = end;
138 	return 0;
139 }
140 
141 void
gray_expand_argv(pam_handle_t * pamh,int argc,const char ** argv,gray_slist_t slist)142 gray_expand_argv(pam_handle_t *pamh, int argc, const char **argv,
143 		 gray_slist_t slist)
144 {
145 	int i;
146 
147 	for (i = 0; i < argc; i++) {
148 		if (i > 0)
149 			gray_slist_append_char(slist, ' ');
150 		if (strchr(argv[i], '$') == 0)
151 			gray_slist_append(slist, argv[i], strlen(argv[i]));
152 		else {
153 			const char *p;
154 
155 			for (p = argv[i]; *p; p++) {
156 				if (*p == '\\') {
157 					p++;
158 					gray_slist_append_char(slist, *p);
159 				} else if (*p == '$') {
160 					if (get_variable(pamh, p, slist, &p))
161 						gray_slist_append_char(slist,
162 								       *p);
163 					else
164 						p--;
165 				} else
166 					gray_slist_append_char(slist, *p);
167 			}
168 		}
169 	}
170 }
171 
172 void
gray_expand_string(pam_handle_t * pamh,const char * str,gray_slist_t slist)173 gray_expand_string(pam_handle_t *pamh, const char *str, gray_slist_t slist)
174 {
175 	const char *p;
176 #define FLUSH() gray_slist_append(slist, str, p - str); str = p
177 
178 	for (p = str; *p; ) {
179 		if (*p == '\\') {
180 			FLUSH();
181 			p++;
182 			if (*p) {
183 				gray_slist_append_char(slist, *p);
184 				p++;
185 			} else {
186 				gray_slist_append_char(slist, '\\');
187 				break;
188 			}
189 			str = p;
190 		} else if (*p == '$') {
191 			FLUSH();
192 			if (get_variable(pamh, p, slist, &p)) {
193 				gray_slist_append_char(slist, *p);
194 				p++;
195 			}
196 			str = p;
197 		} else
198 			p++;
199 	}
200 	FLUSH();
201 }
202