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