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-2015	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 
43 static int has_setuid_bit(char * path);
44 static void *exec_func(void *arg);
45 
46 /*
47  * thread information for pool_thread
48  */
49 typedef struct {
50 	void *(*start_routine)(void *);
51 	void *arg;
52 } WdThreadInfo;
53 
54 
wd_check_network_command_configurations(void)55 void wd_check_network_command_configurations(void)
56 {
57 	char path[128];
58 	char* command;
59 
60 	if (pool_config->use_watchdog == 0)
61 		return;
62 	/*
63 	 * If delegate IP is not assigned to the node
64 	 * the configuration is not used
65 	 */
66 	if (strlen(pool_config->delegate_IP) == 0)
67 		return;
68 
69 	/* check setuid bit of ifup command */
70 	command = wd_get_cmd(pool_config->if_up_cmd);
71 	if (command)
72 	{
73 		snprintf(path, sizeof(path), "%s/%s", pool_config->if_cmd_path, command);
74 		pfree(command);
75 		if (! has_setuid_bit(path))
76 		{
77 			ereport(WARNING,
78 				(errmsg("checking setuid bit of if_up_cmd"),
79 					 errdetail("ifup[%s] doesn't have setuid bit", path)));
80 		}
81 	}
82 	else
83 	{
84 		ereport(FATAL,
85 			(errmsg("invalid configuration for if_up_cmd parameter"),
86 					errdetail("unable to get command from \"%s\"",pool_config->if_up_cmd)));
87 	}
88 	/* check setuid bit of ifdown command */
89 
90 	command = wd_get_cmd(pool_config->if_down_cmd);
91 	if (command)
92 	{
93 		snprintf(path, sizeof(path), "%s/%s", pool_config->if_cmd_path, command);
94 		pfree(command);
95 		if (! has_setuid_bit(path))
96 		{
97 			ereport(WARNING,
98 				(errmsg("checking setuid bit of if_down_cmd"),
99 					 errdetail("ifdown[%s] doesn't have setuid bit", path)));
100 		}
101 	}
102 	else
103 	{
104 		ereport(FATAL,
105 			(errmsg("invalid configuration for if_down_cmd parameter"),
106 					errdetail("unable to get command from \"%s\"",pool_config->if_down_cmd)));
107 	}
108 
109 	/* check setuid bit of arping command */
110 	command = wd_get_cmd(pool_config->arping_cmd);
111 	if (command)
112 	{
113 		snprintf(path, sizeof(path), "%s/%s", pool_config->arping_path, command);
114 		pfree(command);
115 		if (! has_setuid_bit(path))
116 		{
117 			ereport(WARNING,
118 				(errmsg("checking setuid bit of arping command"),
119 					 errdetail("arping[%s] doesn't have setuid bit", path)));
120 		}
121 	}
122 	else
123 	{
124 		ereport(FATAL,
125 			(errmsg("invalid configuration for arping_cmd parameter"),
126 					errdetail("unable to get command from \"%s\"",pool_config->arping_cmd)));
127 	}
128 
129 }
130 
131 /*
132  * if the file has setuid bit and the owner is root, it returns 1, otherwise returns 0
133  */
134 static int
has_setuid_bit(char * path)135 has_setuid_bit(char * path)
136 {
137 	struct stat buf;
138 	if (stat(path,&buf) < 0)
139 	{
140 		ereport(FATAL,
141 			(return_code(1),
142 				 errmsg("has_setuid_bit: command '%s' not found", path)));
143 	}
144 	return ((buf.st_uid == 0) && (S_ISREG(buf.st_mode)) && (buf.st_mode & S_ISUID))?1:0;
145 }
146 
147 
148 #ifdef USE_SSL
149 /* HMAC SHA-256*/
calculate_hmac_sha256(const char * data,int len,char * buf)150 static void calculate_hmac_sha256(const char *data, int len, char *buf)
151 {
152 	char* key = pool_config->wd_authkey;
153 	char str[WD_AUTH_HASH_LEN/2];
154 	unsigned int res_len = WD_AUTH_HASH_LEN;
155 	HMAC_CTX *ctx = NULL;
156 
157 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
158 	ctx = HMAC_CTX_new();
159 	HMAC_CTX_reset(ctx);
160 #else
161 	HMAC_CTX ctx_obj;
162 	ctx = &ctx_obj;
163 	HMAC_CTX_init(ctx);
164 #endif
165 	HMAC_Init_ex(ctx, key, strlen(key), EVP_sha256(), NULL);
166 	HMAC_Update(ctx, (unsigned char*)data, len);
167 	HMAC_Final(ctx, (unsigned char*)str, &res_len);
168 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined (LIBRESSL_VERSION_NUMBER))
169 	HMAC_CTX_reset(ctx);
170 	HMAC_CTX_free(ctx);
171 #else
172 	HMAC_CTX_cleanup(ctx);
173 #endif
174 	bytesToHex(str,32,buf);
175 	buf[WD_AUTH_HASH_LEN] = '\0';
176 }
177 
178 void
wd_calc_hash(const char * str,int len,char * buf)179 wd_calc_hash(const char *str, int len, char *buf)
180 {
181 	calculate_hmac_sha256(str, len, buf);
182 }
183 #else
184 /* calculate hash for authentication using packet contents */
185 void
wd_calc_hash(const char * str,int len,char * buf)186 wd_calc_hash(const char *str, int len, char *buf)
187 {
188 	char pass[(MAX_PASSWORD_SIZE + 1) / 2];
189 	char username[(MAX_PASSWORD_SIZE + 1) / 2];
190 	size_t pass_len;
191 	size_t username_len;
192 	size_t authkey_len;
193 	char tmp_buf[(MD5_PASSWD_LEN+1)*2];
194 	/* use first half of authkey as username, last half as password */
195 	authkey_len = strlen(pool_config->wd_authkey);
196 
197 	if (len <= 0 || authkey_len <= 0)
198 		goto wd_calc_hash_error;
199 
200 	username_len = authkey_len / 2;
201 	pass_len = authkey_len - username_len;
202 	if ( snprintf(username, username_len + 1, "%s", pool_config->wd_authkey) < 0
203 		|| snprintf(pass, pass_len + 1, "%s", pool_config->wd_authkey + username_len) < 0)
204 		goto wd_calc_hash_error;
205 
206 	/* calculate hash using md5 encrypt */
207 	if (! pool_md5_encrypt(pass, username, strlen(username), tmp_buf + MD5_PASSWD_LEN + 1))
208 		goto wd_calc_hash_error;
209 
210 	tmp_buf[sizeof(tmp_buf)-1] = '\0';
211 
212 	if (! pool_md5_encrypt(tmp_buf+MD5_PASSWD_LEN+1, str, len, tmp_buf))
213 		goto wd_calc_hash_error;
214 
215 	memcpy(buf, tmp_buf, MD5_PASSWD_LEN);
216 	buf[MD5_PASSWD_LEN] = '\0';
217 
218 	return;
219 
220 wd_calc_hash_error:
221 	buf[0] = '\0';
222 	return;
223 }
224 #endif
225 
226 /*
227  * string_replace:
228  * returns the new palloced string after replacing all
229  * occurances of pattern in string with replacement string
230  */
231 char *
string_replace(const char * string,const char * pattern,const char * replacement)232 string_replace(const char *string, const char *pattern, const char *replacement)
233 {
234 	char *tok = NULL;
235 	char *newstr = NULL;
236 	char *oldstr = NULL;
237 	char *head = NULL;
238 	size_t pat_len,rep_len;
239 
240 	newstr = pstrdup(string);
241 	/* bail out if no pattern or replacement is given */
242 	if ( pattern == NULL || replacement == NULL )
243 		return newstr;
244 
245 	pat_len = strlen(pattern);
246 	rep_len = strlen(replacement);
247 
248 	head = newstr;
249 	while ( (tok = strstr(head,pattern)))
250 	{
251 		oldstr = newstr;
252 		newstr = palloc ( strlen ( oldstr ) - pat_len + rep_len + 1 );
253 
254 		memcpy(newstr, oldstr, tok - oldstr );
255 		memcpy(newstr + (tok - oldstr), replacement, rep_len );
256 		memcpy(newstr + (tok - oldstr) + rep_len, tok + pat_len, strlen(oldstr) - pat_len - (tok - oldstr));
257 		/* put the string terminator */
258 		memset( newstr + strlen (oldstr) - pat_len + rep_len , 0, 1 );
259 		/* move back head right after the last replacement */
260 		head = newstr + (tok - oldstr) + rep_len;
261 		pfree(oldstr);
262 	}
263 	return newstr;
264 }
265 
266 /*
267  * The function is wrapper over pthread_create.
268  */
watchdog_thread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)269 int watchdog_thread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
270 {
271 	WdThreadInfo* thread_arg = palloc(sizeof(WdThreadInfo));
272 	thread_arg->arg = arg;
273 	thread_arg->start_routine = start_routine;
274 	return pthread_create(thread, attr, exec_func, thread_arg);
275 }
276 
277 static void *
exec_func(void * arg)278 exec_func(void *arg)
279 {
280 	WdThreadInfo* thread_arg = (WdThreadInfo*) arg;
281 	Assert(thread_arg != NULL);
282 	return thread_arg->start_routine(thread_arg->arg);
283 }
284 
285