1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25 #include <policy_server.h>
26
27 #include <eval_context.h>
28 #include <addr_lib.h>
29 #include <communication.h>
30
31 #include <assert.h>
32 #include <logging.h>
33 #include <string_lib.h>
34 #include <file_lib.h>
35
36 //*******************************************************************
37 // POLICY SERVER VARIABLES:
38 //*******************************************************************
39
40 static char *POLICY_SERVER = NULL; // full bootstrap argument
41 static char *POLICY_SERVER_HOST = NULL; // only host part, if present
42 static char POLICY_SERVER_PORT[CF_MAX_PORT_LEN]; // only port part
43 static char POLICY_SERVER_IP[CF_MAX_IP_LEN]; // resolved IP
44
45
46 //*******************************************************************
47 // POLICY SERVER SET FUNCTION:
48 //*******************************************************************
49
is_whitespace_empty(const char * str)50 static bool is_whitespace_empty(const char *str) {
51 if(NULL_OR_EMPTY(str))
52 {
53 return true;
54 }
55 while (str[0] != '\0')
56 {
57 if (!isspace(str[0]))
58 {
59 return false;
60 }
61 ++str;
62 }
63 return true;
64 }
65
66 /**
67 * @brief Sets both internal C variables as well as policy sys variables.
68 *
69 * Called at bootstrap and after reading policy_server.dat.
70 * Changes sys.policy_hub and sys.policy_hub_port.
71 * NULL is a valid input for new_policy_server, everything will be freed and
72 * set to NULL.
73 *
74 * @param ctx EvalContext is used to set related variables
75 * @param new_policy_server can be 'host:port', same as policy_server.dat
76 */
PolicyServerSet(const char * new_policy_server)77 void PolicyServerSet(const char *new_policy_server)
78 {
79 // Clean up static variables:
80 free(POLICY_SERVER);
81 free(POLICY_SERVER_HOST);
82 POLICY_SERVER = NULL;
83 POLICY_SERVER_HOST = NULL;
84
85 POLICY_SERVER_IP[0] = '\0';
86 POLICY_SERVER_PORT[0] = '\0';
87
88 if (is_whitespace_empty(new_policy_server))
89 {
90 return;
91 }
92 else
93 {// Set POLICY_SERVER to be bootstrap argument/policy_server.dat contents
94 POLICY_SERVER = xstrdup(new_policy_server);
95 }
96
97 // Parse policy server in a separate buffer:
98 char *host_or_ip, *port;
99 char *buffer = xstrdup(new_policy_server);
100
101 AddressType address_type = ParseHostPort(buffer, &host_or_ip, &port);
102
103 if (address_type == ADDRESS_TYPE_OTHER)
104 {
105 POLICY_SERVER_HOST = xstrdup(host_or_ip);
106 }
107 else // ADDRESS_TYPE_IPV4 or ADDRESS_TYPE_IPV6
108 {
109 assert(strlen(host_or_ip) < sizeof(POLICY_SERVER_IP));
110 StringCopy(host_or_ip, POLICY_SERVER_IP, sizeof(POLICY_SERVER_IP));
111 }
112
113 if ( ! NULL_OR_EMPTY(port) )
114 {
115 if(strlen(port) < CF_MAX_PORT_LEN)
116 {
117 strcpy(POLICY_SERVER_PORT, port);
118 }
119 else
120 {
121 Log(LOG_LEVEL_WARNING,
122 "Too long port number in PolicyServerSet: '%s'",
123 port);
124 }
125 }
126
127 free(buffer);
128 }
129
130
131 //*******************************************************************
132 // POLICY SERVER GET FUNCTIONS:
133 //*******************************************************************
134
CheckEmptyReturn(char * s)135 static char *CheckEmptyReturn(char *s)
136 {
137 return (NULL_OR_EMPTY(s)) ? NULL : s;
138 }
139
140 /**
141 * @brief Used to access the internal POLICY_SERVER variable.
142 * @return Read-only string, can be 'host:port', same as policy_server.dat
143 * NULL if not bootstrapped ( not set ).
144 */
PolicyServerGet()145 const char *PolicyServerGet()
146 {
147 return POLICY_SERVER; // Don't use CheckedReturn, var should be NULL!
148 }
149
150 /**
151 * @brief Gets the IP address of policy server, does lookup if necessary.
152 * @return Read-only string, can be IPv4 or IPv6.
153 * NULL if not bootstrapped or lookup failed.
154 */
PolicyServerGetIP()155 const char *PolicyServerGetIP()
156 {
157 if (POLICY_SERVER_HOST == NULL)
158 {
159 return CheckEmptyReturn(POLICY_SERVER_IP);
160 }
161 assert(POLICY_SERVER_HOST[0] != '\0');
162 int ret = Hostname2IPString(POLICY_SERVER_IP, POLICY_SERVER_HOST,
163 CF_MAX_IP_LEN);
164 if (ret != 0) // Lookup failed
165 {
166 return NULL;
167 }
168 return CheckEmptyReturn(POLICY_SERVER_IP);
169 }
170
171 /**
172 * @brief Gets the host part of what was bootstrapped to (without port).
173 * @return Read-only string, hostname part of bootstrap argument.
174 * NULL if not bootstrapped or bootstrapped to IP.
175 */
PolicyServerGetHost()176 const char *PolicyServerGetHost()
177 {
178 return POLICY_SERVER_HOST; // Don't use CheckedReturn, var should be NULL!
179 }
180
181 /**
182 * @brief Gets the port part of the policy server.
183 * @return Read-only null terminated string of port number.
184 * NULL if port not specified, or not bootstrapped at all.
185 */
PolicyServerGetPort()186 const char *PolicyServerGetPort()
187 {
188 return CheckEmptyReturn(POLICY_SERVER_PORT);
189 }
190
191 //*******************************************************************
192 // POLICY SERVER FILE FUNCTIONS:
193 //*******************************************************************
194
PolicyServerFilename(const char * workdir)195 static char *PolicyServerFilename(const char *workdir)
196 {
197 return StringFormat("%s%cpolicy_server.dat", workdir, FILE_SEPARATOR);
198 }
199
200 /**
201 * @brief Reads the policy_server.dat file.
202 * @param[in] workdir the directory of policy_server.dat usually GetWorkDir()
203 * @return Trimmed contents of policy_server.dat file. Null terminated.
204 */
PolicyServerReadFile(const char * workdir)205 char *PolicyServerReadFile(const char *workdir)
206 {
207 char contents[CF_MAX_SERVER_LEN] = "";
208
209 char *filename = PolicyServerFilename(workdir);
210 FILE *fp = safe_fopen(filename, "r");
211 if (fp == NULL)
212 {
213 Log( LOG_LEVEL_VERBOSE, "Could not open file '%s' (fopen: %s)",
214 filename, GetErrorStr() );
215 free(filename);
216 return NULL;
217 }
218
219 if (fgets(contents, CF_MAX_SERVER_LEN, fp) == NULL)
220 {
221 Log( LOG_LEVEL_VERBOSE, "Could not read file '%s' (fgets: %s)",
222 filename, GetErrorStr() );
223 free(filename);
224 fclose(fp);
225 return NULL;
226 }
227
228 free(filename);
229 fclose(fp);
230 char *start = TrimWhitespace(contents);
231 return xstrdup(start);
232 }
233
234
235 /**
236 * @brief Reads and parses the policy_server.dat file.
237 *
238 * @code{.c}
239 * //Typical usage:
240 * char *host, *port;
241 * bool file_read = PolicyServerParseFile(GetWorkDir(), &host, &port);
242 * printf( "host is %s", file_read ? host : "unavailable" );
243 * free(host); free(port);
244 * @endcode
245 *
246 * @param[in] workdir The directory of policy_server.dat usually GetWorkDir()
247 * @param[out] host pointer at this address will be hostname string (strdup)
248 * @param[out] port pointer at this address will be port string (strdup)
249 * @attention host* and port* must be freed.
250 * @return Boolean indicating success.
251 */
PolicyServerParseFile(const char * workdir,char ** host,char ** port)252 bool PolicyServerParseFile(const char *workdir, char **host, char **port)
253 {
254 char *contents = PolicyServerReadFile(workdir);
255 if (contents == NULL)
256 {
257 return false;
258 }
259 (*host) = NULL;
260 (*port) = NULL;
261
262 ParseHostPort(contents, host, port);
263
264 // The file did not contain a host
265 if (*host == NULL)
266 {
267 return false;
268 }
269
270 (*host) = xstrdup(*host);
271 if (*port != NULL)
272 {
273 (*port) = xstrdup(*port);
274 }
275 free(contents);
276 return true;
277 }
278
279 /**
280 * @brief Reads and parses the policy_server.dat file.
281 *
282 * @param[in] workdir The directory of policy_server.dat usually GetWorkDir()
283 * @param[out] ipaddr pointer at this address will be hostname string (strdup)
284 * @param[out] port pointer at this address will be port string (strdup)
285 * @attention ipaddr* and port* must be freed.
286 * @return Boolean indicating success.
287 * @see PolicyServerParseFile
288 */
PolicyServerLookUpFile(const char * workdir,char ** ipaddr,char ** port)289 bool PolicyServerLookUpFile(const char *workdir, char **ipaddr, char **port)
290 {
291 char *host;
292 bool file_read = PolicyServerParseFile(workdir, &host, port);
293 if (file_read == false)
294 {
295 return false;
296 }
297 char tmp_ipaddr[CF_MAX_IP_LEN];
298 if (Hostname2IPString(tmp_ipaddr, host, sizeof(tmp_ipaddr)) == -1)
299 {
300 Log(LOG_LEVEL_ERR,
301 "Unable to resolve policy server host: %s", host);
302 free(host);
303 free(*port);
304 (*port) = NULL;
305 return false;
306 }
307 (*ipaddr) = xstrdup(tmp_ipaddr);
308 free(host);
309 return true;
310 }
311
312 /**
313 * @brief Write new_policy_server to the policy_server.dat file.
314 * @param[in] workdir The directory of policy_server.dat, usually GetWorkDir()
315 * @param[in] new_policy_server The host:port string defining the server
316 * @return True if successful
317 */
PolicyServerWriteFile(const char * workdir,const char * new_policy_server)318 bool PolicyServerWriteFile(const char *workdir, const char *new_policy_server)
319 {
320 char *filename = PolicyServerFilename(workdir);
321
322 FILE *file = safe_fopen(filename, "w");
323 if (file == NULL)
324 {
325 Log(LOG_LEVEL_ERR, "Unable to write policy server file '%s' (fopen: %s)", filename, GetErrorStr());
326 free(filename);
327 return false;
328 }
329
330 fprintf(file, "%s\n", new_policy_server);
331 fclose(file);
332
333 free(filename);
334 return true;
335 }
336
337 /**
338 * @brief Remove the policy_server.dat file
339 * @param[in] workdir The directory of policy_server.dat, usually GetWorkDir()
340 * @return True if successful
341 */
PolicyServerRemoveFile(const char * workdir)342 bool PolicyServerRemoveFile(const char *workdir)
343 {
344 char *filename = PolicyServerFilename(workdir);
345
346 if (unlink(filename) != 0)
347 {
348 Log(LOG_LEVEL_ERR, "Unable to remove file '%s'. (unlink: %s)", filename, GetErrorStr());
349 free(filename);
350 return false;
351 }
352
353 free(filename);
354 return true;
355 }
356