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