1 /*
2  * $Header$
3  *
4  * Handles watchdog connection, and protocol communication with pgpool-II
5  *
6  * pgpool: a language independent connection pool server for PostgreSQL
7  * written by Tatsuo Ishii
8  *
9  * Copyright (c) 2003-2018	PgPool Global Development Group
10  *
11  * Permission to use, copy, modify, and distribute this software and
12  * its documentation for any purpose and without fee is hereby
13  * granted, provided that the above copyright notice appear in all
14  * copies and that both that copyright notice and this permission
15  * notice appear in supporting documentation, and that the name of the
16  * author not be used in advertising or publicity pertaining to
17  * distribution of the software without specific, written prior
18  * permission. The author makes no representations about the
19  * suitability of this software for any purpose.  It is provided "as
20  * is" without express or implied warranty.
21  *
22  */
23 #include <pthread.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <sys/un.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <sys/wait.h>
33 #include <errno.h>
34 
35 #include "pool.h"
36 #include "auth/md5.h"
37 #include "utils/elog.h"
38 #include "utils/palloc.h"
39 #include "utils/memutils.h"
40 #include "pool_config.h"
41 #include "watchdog/wd_utils.h"
42 #include "utils/ssl_utils.h"
43 
44 static int	has_setuid_bit(char *path);
45 static void *exec_func(void *arg);
46 
47 /*
48  * thread information for pool_thread
49  */
50 typedef struct
51 {
52 	void	   *(*start_routine) (void *);
53 	void	   *arg;
54 }			WdThreadInfo;
55 
56 
57 void
wd_check_network_command_configurations(void)58 wd_check_network_command_configurations(void)
59 {
60 	char		path[128];
61 	char	   *command;
62 
63 	if (pool_config->use_watchdog == 0)
64 		return;
65 
66 	/*
67 	 * If delegate IP is not assigned to the node the configuration is not
68 	 * used
69 	 */
70 	if (strlen(pool_config->delegate_IP) == 0)
71 		return;
72 
73 	/* check setuid bit of ifup command */
74 	command = wd_get_cmd(pool_config->if_up_cmd);
75 
76 	if (command)
77 	{
78 		if (command[0] == '/')
79 		{
80 			pfree(command);
81 			return;
82 		}
83 
84 		snprintf(path, sizeof(path), "%s/%s", pool_config->if_cmd_path, command);
85 		pfree(command);
86 		if (!has_setuid_bit(path))
87 		{
88 			ereport(WARNING,
89 					(errmsg("checking setuid bit of if_up_cmd"),
90 					 errdetail("ifup[%s] doesn't have setuid bit", path)));
91 		}
92 	}
93 	else
94 	{
95 		ereport(FATAL,
96 				(errmsg("invalid configuration for if_up_cmd parameter"),
97 				 errdetail("unable to get command from \"%s\"", pool_config->if_up_cmd)));
98 	}
99 	/* check setuid bit of ifdown command */
100 
101 	command = wd_get_cmd(pool_config->if_down_cmd);
102 	if (command)
103 	{
104 		snprintf(path, sizeof(path), "%s/%s", pool_config->if_cmd_path, command);
105 		pfree(command);
106 		if (!has_setuid_bit(path))
107 		{
108 			ereport(WARNING,
109 					(errmsg("checking setuid bit of if_down_cmd"),
110 					 errdetail("ifdown[%s] doesn't have setuid bit", path)));
111 		}
112 	}
113 	else
114 	{
115 		ereport(FATAL,
116 				(errmsg("invalid configuration for if_down_cmd parameter"),
117 				 errdetail("unable to get command from \"%s\"", pool_config->if_down_cmd)));
118 	}
119 
120 	/* check setuid bit of arping command */
121 	command = wd_get_cmd(pool_config->arping_cmd);
122 	if (command)
123 	{
124 		snprintf(path, sizeof(path), "%s/%s", pool_config->arping_path, command);
125 		pfree(command);
126 		if (!has_setuid_bit(path))
127 		{
128 			ereport(WARNING,
129 					(errmsg("checking setuid bit of arping command"),
130 					 errdetail("arping[%s] doesn't have setuid bit", path)));
131 		}
132 	}
133 	else
134 	{
135 		ereport(FATAL,
136 				(errmsg("invalid configuration for arping_cmd parameter"),
137 				 errdetail("unable to get command from \"%s\"", pool_config->arping_cmd)));
138 	}
139 
140 }
141 
142 /*
143  * if the file has setuid bit and the owner is root, it returns 1, otherwise returns 0
144  */
145 static int
has_setuid_bit(char * path)146 has_setuid_bit(char *path)
147 {
148 	struct stat buf;
149 
150 	if (stat(path, &buf) < 0)
151 	{
152 		ereport(FATAL,
153 				(return_code(1),
154 				 errmsg("has_setuid_bit: command '%s' not found", path)));
155 	}
156 	return ((buf.st_uid == 0) && (S_ISREG(buf.st_mode)) && (buf.st_mode & S_ISUID)) ? 1 : 0;
157 }
158 
159 
160 #ifdef USE_SSL
161 
162 void
wd_calc_hash(const char * str,int len,char * buf)163 wd_calc_hash(const char *str, int len, char *buf)
164 {
165 	calculate_hmac_sha256(str, len, buf);
166 }
167 #else
168 
169 /* calculate hash for authentication using packet contents */
170 void
wd_calc_hash(const char * str,int len,char * buf)171 wd_calc_hash(const char *str, int len, char *buf)
172 {
173 	char		pass[(MAX_PASSWORD_SIZE + 1) / 2];
174 	char		username[(MAX_PASSWORD_SIZE + 1) / 2];
175 	size_t		pass_len;
176 	size_t		username_len;
177 	size_t		authkey_len;
178 	char		tmp_buf[(MD5_PASSWD_LEN + 1) * 2];
179 
180 	/* use first half of authkey as username, last half as password */
181 	authkey_len = strlen(pool_config->wd_authkey);
182 
183 	if (len <= 0 || authkey_len <= 0)
184 		goto wd_calc_hash_error;
185 
186 	username_len = authkey_len / 2;
187 	pass_len = authkey_len - username_len;
188 	if (snprintf(username, username_len + 1, "%s", pool_config->wd_authkey) < 0
189 		|| snprintf(pass, pass_len + 1, "%s", pool_config->wd_authkey + username_len) < 0)
190 		goto wd_calc_hash_error;
191 
192 	/* calculate hash using md5 encrypt */
193 	if (!pool_md5_encrypt(pass, username, strlen(username), tmp_buf + MD5_PASSWD_LEN + 1))
194 		goto wd_calc_hash_error;
195 
196 	tmp_buf[sizeof(tmp_buf) - 1] = '\0';
197 
198 	if (!pool_md5_encrypt(tmp_buf + MD5_PASSWD_LEN + 1, str, len, tmp_buf))
199 		goto wd_calc_hash_error;
200 
201 	memcpy(buf, tmp_buf, MD5_PASSWD_LEN);
202 	buf[MD5_PASSWD_LEN] = '\0';
203 
204 	return;
205 
206 wd_calc_hash_error:
207 	buf[0] = '\0';
208 	return;
209 }
210 #endif
211 
212 /*
213  * string_replace:
214  * returns the new palloced string after replacing all
215  * occurances of pattern in string with replacement string
216  */
217 char *
string_replace(const char * string,const char * pattern,const char * replacement)218 string_replace(const char *string, const char *pattern, const char *replacement)
219 {
220 	char	   *tok = NULL;
221 	char	   *newstr = NULL;
222 	char	   *oldstr = NULL;
223 	char	   *head = NULL;
224 	size_t		pat_len,
225 				rep_len;
226 
227 	newstr = pstrdup(string);
228 	/* bail out if no pattern or replacement is given */
229 	if (pattern == NULL || replacement == NULL)
230 		return newstr;
231 
232 	pat_len = strlen(pattern);
233 	rep_len = strlen(replacement);
234 
235 	head = newstr;
236 	while ((tok = strstr(head, pattern)))
237 	{
238 		oldstr = newstr;
239 		newstr = palloc(strlen(oldstr) - pat_len + rep_len + 1);
240 
241 		memcpy(newstr, oldstr, tok - oldstr);
242 		memcpy(newstr + (tok - oldstr), replacement, rep_len);
243 		memcpy(newstr + (tok - oldstr) + rep_len, tok + pat_len, strlen(oldstr) - pat_len - (tok - oldstr));
244 		/* put the string terminator */
245 		memset(newstr + strlen(oldstr) - pat_len + rep_len, 0, 1);
246 		/* move back head right after the last replacement */
247 		head = newstr + (tok - oldstr) + rep_len;
248 		pfree(oldstr);
249 	}
250 	return newstr;
251 }
252 
253 /*
254  * The function is wrapper over pthread_create.
255  */
watchdog_thread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)256 int			watchdog_thread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)
257 {
258 	WdThreadInfo *thread_arg = palloc(sizeof(WdThreadInfo));
259 
260 	thread_arg->arg = arg;
261 	thread_arg->start_routine = start_routine;
262 	return pthread_create(thread, attr, exec_func, thread_arg);
263 }
264 
265 static void *
exec_func(void * arg)266 exec_func(void *arg)
267 {
268 	WdThreadInfo *thread_arg = (WdThreadInfo *) arg;
269 
270 	Assert(thread_arg != NULL);
271 	return thread_arg->start_routine(thread_arg->arg);
272 }
273