1 /* This file is part of pam-modules.
2    Copyright (C) 2005-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 "pam_sql.h"
18 
19 /* indicate the following groups are defined */
20 #define PAM_SM_AUTH
21 #define PAM_SM_SESSION
22 
23 #define CNTL_AUTHTOK      0x0010
24 
25 static int cntl_flags;
26 long gpam_sql_debug_level;
27 char *gpam_sql_config_file = SYSCONFDIR "/pam_sql.conf";
28 
29 struct pam_opt pam_opt[] = {
30 	{ PAM_OPTSTR(debug), pam_opt_long, &debug_level },
31 	{ PAM_OPTSTR(debug), pam_opt_const, &debug_level, { 1 } },
32 	{ PAM_OPTSTR(audit), pam_opt_const, &debug_level, { 100 } },
33 	{ PAM_OPTSTR(waitdebug), pam_opt_null, NULL, { 0 },
34 	  gray_wait_debug_fun },
35 	{ PAM_OPTSTR(use_authtok), pam_opt_bitmask, &cntl_flags,
36 	  { CNTL_AUTHTOK } },
37 	{ PAM_OPTSTR(config), pam_opt_string, &gpam_sql_config_file },
38 	{ NULL }
39 };
40 
41 static void
_pam_parse(int argc,const char ** argv)42 _pam_parse(int argc, const char **argv)
43 {
44 	cntl_flags = 0;
45 	debug_level = 0;
46 	gpam_sql_config_file = SYSCONFDIR "/pam_sql.conf";
47 	gray_log_init(0, gpam_sql_module_name, LOG_AUTHPRIV);
48 	gray_parseopt(pam_opt, argc, argv);
49 }
50 
51 
52 static int
_pam_get_password(pam_handle_t * pamh,char ** password,const char * prompt)53 _pam_get_password(pam_handle_t *pamh, char **password, const char *prompt)
54 {
55 	char *item, *token;
56 	int retval;
57 	struct pam_message msg[3], *pmsg[3];
58 	struct pam_response *resp;
59 	int i, replies;
60 
61 	DEBUG(90,("enter _pam_get_password"));
62 
63 	if (cntl_flags & CNTL_AUTHTOK) {
64 		/*
65 		 * get the password from the PAM item
66 		 */
67 		retval = pam_get_item(pamh, PAM_AUTHTOK,
68 				      (const void **) &item);
69 		if (retval != PAM_SUCCESS) {
70 			/* very strange. */
71 			_pam_log(LOG_ALERT,
72 				 "can't retrieve password item: %s",
73 				 pam_strerror(pamh, retval));
74 			return retval;
75 		} else if (item != NULL) {
76 			*password = item;
77 			item = NULL;
78 			return PAM_SUCCESS;
79 		} else
80 			return PAM_AUTHTOK_RECOVER_ERR;
81 	}
82 
83 	/*
84 	 * ask user for the password
85 	 */
86 	/* prepare to converse */
87 
88 	i = 0;
89 	pmsg[i] = &msg[i];
90 	msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
91 	msg[i++].msg = (const void*)prompt;
92 	replies = 1;
93 
94 	/* run conversation */
95 	resp = NULL;
96 	token = NULL;
97 	retval = gray_converse(pamh, i, pmsg, &resp);
98 
99 	if (resp != NULL && resp[i - replies].resp) {
100 		if (retval == PAM_SUCCESS) { 	/* a good conversation */
101 			token = XSTRDUP(resp[i - replies].resp);
102 			DEBUG(100,("app returned [%s]", token));
103 			pam_set_item(pamh, PAM_AUTHTOK, token);
104 			PAM_DROP_REPLY(resp, 1);
105 		} else {
106 			_pam_log(LOG_ERR, "conversation error: %s",
107 				 pam_strerror(pamh, retval));
108 		}
109 
110 	} else {
111 		retval = (retval == PAM_SUCCESS)
112 			? PAM_AUTHTOK_RECOVER_ERR : retval;
113 	}
114 
115 	if (retval == PAM_SUCCESS) {
116 		/*
117 		 * keep password as data specific to this module. pam_end()
118 		 * will arrange to clean it up.
119 		 */
120 		retval = pam_set_data(pamh, "password",
121 				      (void *)token,
122 				      gray_cleanup_string);
123 		if (retval != PAM_SUCCESS) {
124 			_pam_log(LOG_CRIT,
125 			         "can't keep password: %s",
126 				 pam_strerror(pamh, retval));
127 			gray_pam_delete(token);
128 		} else {
129 			*password = token;
130 			token = NULL;	/* break link to password */
131 		}
132 	} else {
133 		_pam_log(LOG_ERR,
134 			 "unable to obtain a password: %s",
135 			 pam_strerror(pamh, retval));
136 	}
137 
138 	DEBUG(90,("exit _pam_get_password: %d", retval));
139 	return retval;
140 }
141 
142 
143 /* Configuration */
144 static struct gray_env *config_env;
145 
146 char *
gpam_sql_find_config(const char * name)147 gpam_sql_find_config(const char *name)
148 {
149 	return gray_env_get(config_env, name);
150 }
151 
152 int
gpam_sql_check_boolean_config(const char * name,int defval)153 gpam_sql_check_boolean_config(const char *name, int defval)
154 {
155 	const char *value = gpam_sql_find_config(name);
156 	if (value)
157 		defval = gray_boolean_true_p(value);
158 	return defval;
159 }
160 
161 
162 const char *
gpam_sql_get_query(pam_handle_t * pamh,const char * name,gray_slist_t * pslist,int required)163 gpam_sql_get_query(pam_handle_t *pamh, const char *name, gray_slist_t *pslist,
164 		   int required)
165 {
166 	gray_slist_t slist;
167 	const char *query = gpam_sql_find_config(name);
168 
169  	if (!query) {
170 		if (required)
171 			gray_raise("%s: %s not defined", gpam_sql_config_file, name);
172 		return NULL;
173 	}
174 
175 	slist = gray_slist_create();
176 	gray_expand_string(pamh, query, slist);
177 	gray_slist_append_char(slist, 0);
178 	*pslist = slist;
179 	return gray_slist_finish(slist);
180 }
181 
182 static const char *
get_query2(pam_handle_t * pamh,const char * name1,const char * name2,gray_slist_t * pslist,int required)183 get_query2(pam_handle_t *pamh, const char *name1, const char *name2,
184 	   gray_slist_t *pslist, int required)
185 {
186 	gray_slist_t slist;
187 	const char *query = gpam_sql_find_config(name1);
188 
189 	if (!query)
190 		query = gpam_sql_find_config(name2);
191 
192  	if (!query) {
193 		if (required)
194 			gray_raise("%s: %s not defined",
195 			           gpam_sql_config_file, name1);
196 		return NULL;
197 	}
198 
199 	slist = gray_slist_create();
200 	gray_expand_string(pamh, query, slist);
201 	gray_slist_append_char(slist, 0);
202 	*pslist = slist;
203 	return gray_slist_finish(slist);
204 }
205 
206 
207 /* --- authentication management functions (only) --- */
208 
209 PAM_EXTERN int
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)210 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
211 {
212 	const char *username;
213 	char *password;
214 	int retval = PAM_AUTH_ERR;
215 	gray_pam_init(PAM_SERVICE_ERR);
216 
217 	/* parse arguments */
218 	_pam_parse(argc, argv);
219 
220 	/* Get the username */
221 	retval = pam_get_user(pamh, &username, NULL);
222 	if (retval != PAM_SUCCESS || !username) {
223 		DEBUG(1, ("can not get the username"));
224 		return PAM_SERVICE_ERR;
225 	}
226 
227 	/* Get the password */
228 	if (_pam_get_password(pamh, &password, "Password:"))
229 		return PAM_SERVICE_ERR;
230 
231 	if (retval != PAM_SUCCESS) {
232 		_pam_log(LOG_ERR, "Could not retrive user's password");
233 		return PAM_SERVICE_ERR;
234 	}
235 
236 	if (gray_env_read(gpam_sql_config_file, &config_env))
237 		retval = PAM_SERVICE_ERR;
238 	else {
239 		gray_slist_t slist;
240 		/* FIXME: This comment is needed to pacify
241 		   `make check-sql-config' in doc:
242 		   gpam_sql_find_config("passwd-query") */
243 		retval = gpam_sql_verify_user_pass(pamh, password,
244 					     get_query2(pamh, "passwd-query",
245 					     "query",  &slist, 1));
246 		gray_slist_free(&slist);
247 	}
248 
249 	gray_env_free(config_env);
250 	config_env = NULL;
251 
252 	switch (retval) {
253 	case PAM_ACCT_EXPIRED:
254 		_pam_log(LOG_NOTICE, "user '%s': account expired", username);
255 		break;
256 	case PAM_SUCCESS:
257 		_pam_log(LOG_NOTICE, "user '%s' granted access", username);
258 		break;
259 	default:
260 		_pam_log(LOG_NOTICE, "user '%s' failed to authenticate",
261 			 username);
262 	}
263 
264 	return retval;
265 }
266 
267 PAM_EXTERN int
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)268 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
269 {
270 	return PAM_SUCCESS;
271 }
272 
273 static int
sql_session_mgmt(pam_handle_t * pamh,int flags,int argc,const char ** argv,const char * query_name)274 sql_session_mgmt(pam_handle_t *pamh, int flags,
275 		 int argc, const char **argv, const char *query_name)
276 {
277 	int retval;
278 
279 	gray_pam_init(PAM_SERVICE_ERR);
280 
281 	/* parse arguments */
282 	_pam_parse(argc, argv);
283 
284 	if (gray_env_read(gpam_sql_config_file, &config_env))
285 		retval = PAM_SERVICE_ERR;
286 	else {
287 		gray_slist_t slist;
288 		retval = gpam_sql_acct(pamh,
289 				       gpam_sql_get_query(pamh, query_name,
290 							  &slist, 0));
291 		gray_slist_free(&slist);
292 	}
293 
294 	gray_env_free(config_env);
295 	config_env = NULL;
296 
297 	return retval;
298 }
299 
300 PAM_EXTERN int
pam_sm_open_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)301 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
302 {
303 	/* FIXME: This comment is needed to pacify `make check-sql-config'
304 	   in doc:
305 	   gpam_sql_find_config("session-start-query") */
306 	return sql_session_mgmt(pamh, flags, argc, argv,
307 				"session-start-query");
308 }
309 
310 PAM_EXTERN int
pam_sm_close_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)311 pam_sm_close_session(pam_handle_t *pamh, int flags,
312 		     int argc, const char **argv)
313 {
314 	/* FIXME: This comment is needed to pacify `make check-sql-config'
315 	   in doc:
316 	   gpam_sql_find_config("session-stop-query") */
317 	return sql_session_mgmt(pamh, flags, argc, argv,
318 				"session-stop-query");
319 }
320