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 <exec-config.h>
26 
27 #include <alloc.h>
28 #include <string_lib.h>
29 #include <writer.h>
30 
31 #include <rlist.h>
32 #include <eval_context.h>
33 #include <conversion.h>
34 #include <generic_agent.h> // TODO: fix
35 #include <regex.h> // pcre_free()
36 #include <item_lib.h>
37 
38 
GetIpAddresses(const EvalContext * ctx)39 static char *GetIpAddresses(const EvalContext *ctx)
40 {
41     Writer *ipbuf = StringWriter();
42 
43     for (Item *iptr = EvalContextGetIpAddresses(ctx); iptr != NULL; iptr = iptr->next)
44     {
45         WriterWrite(ipbuf, iptr->name);
46         if (iptr->next != NULL)
47         {
48             WriterWriteChar(ipbuf, ' ');
49         }
50     }
51 
52     return StringWriterClose(ipbuf);
53 }
54 
RegexFree(void * ptr)55 static void RegexFree(void *ptr)
56 {
57     pcre_free(ptr);
58 }
59 
MailFilterFill(const char * str,Seq ** output,Seq ** output_regex,const char * filter_type)60 static void MailFilterFill(const char *str,
61                            Seq **output, Seq **output_regex,
62                            const char *filter_type)
63 {
64     const char *errorstr;
65     int erroffset;
66     pcre *rx = pcre_compile(str,
67                             PCRE_MULTILINE | PCRE_DOTALL | PCRE_ANCHORED,
68                             &errorstr, &erroffset, NULL);
69     if (!rx)
70     {
71         Log(LOG_LEVEL_ERR,
72             "Invalid regular expression in mailfilter_%s: "
73             "pcre_compile() '%s' in expression '%s' (offset: %d). "
74             "Ignoring expression.", filter_type,
75             errorstr, str, erroffset);
76     }
77     else
78     {
79         SeqAppend(*output, xstrdup(str));
80         SeqAppend(*output_regex, rx);
81     }
82 }
83 
RlistMailFilterFill(const Rlist * input,Seq ** output,Seq ** output_regex,const char * filter_type)84 static void RlistMailFilterFill(const Rlist *input,
85                                 Seq **output, Seq **output_regex,
86                                 const char *filter_type)
87 {
88     int len = RlistLen(input);
89 
90     *output = SeqNew(len, &free);
91     *output_regex = SeqNew(len, &RegexFree);
92 
93     const Rlist *ptr = input;
94     for (int i = 0; i < len; i++)
95     {
96         const char *str = ptr->val.item;
97         MailFilterFill(str, output, output_regex, filter_type);
98 
99         ptr = ptr->next;
100     }
101 }
102 
SeqMailFilterFill(const Seq * input,Seq ** output,Seq ** output_regex,const char * filter_type)103 static void SeqMailFilterFill(const Seq *input,
104                            Seq **output, Seq **output_regex,
105                            const char *filter_type)
106 {
107     int len = SeqLength(input);
108 
109     *output = SeqNew(len, &free);
110     *output_regex = SeqNew(len, &RegexFree);
111 
112     for (int i = 0; i < len; i++)
113     {
114         MailFilterFill(SeqAt(input, i), output, output_regex, filter_type);
115     }
116 }
117 
ExecConfigNew(bool scheduled_run,const EvalContext * ctx,const Policy * policy)118 ExecConfig *ExecConfigNew(bool scheduled_run, const EvalContext *ctx, const Policy *policy)
119 {
120     ExecConfig *exec_config = xcalloc(1, sizeof(ExecConfig));
121 
122     exec_config->scheduled_run = scheduled_run;
123     exec_config->exec_command = xstrdup("");
124     exec_config->agent_expireafter = 2 * 60;                   /* two hours */
125 
126     exec_config->mail_server = xstrdup("");
127     exec_config->mail_from_address = xstrdup("");
128     exec_config->mail_to_address = xstrdup("");
129     exec_config->mail_subject = xstrdup("");
130     exec_config->mail_max_lines = 30;
131     exec_config->mailfilter_include = SeqNew(0, &free);
132     exec_config->mailfilter_include_regex = SeqNew(0, &RegexFree);
133     exec_config->mailfilter_exclude = SeqNew(0, &free);
134     exec_config->mailfilter_exclude_regex = SeqNew(0, &RegexFree);
135 
136     exec_config->fq_name = xstrdup(VFQNAME);
137     exec_config->ip_address = xstrdup(VIPADDRESS);
138     exec_config->ip_addresses = GetIpAddresses(ctx);
139 
140     Seq *constraints = ControlBodyConstraints(policy, AGENT_TYPE_EXECUTOR);
141     if (constraints)
142     {
143         for (size_t i = 0; i < SeqLength(constraints); i++)
144         {
145             Constraint *cp = SeqAt(constraints, i);
146 
147             if (!IsDefinedClass(ctx, cp->classes))
148             {
149                 continue;
150             }
151 
152             VarRef *ref = VarRefParseFromScope(cp->lval, "control_executor");
153             DataType t;
154             const void *value = EvalContextVariableGet(ctx, ref, &t);
155             VarRefDestroy(ref);
156 
157             if (t == CF_DATA_TYPE_NONE)
158             {
159                 ProgrammingError("Unknown attribute '%s' in control body,"
160                                  " should have already been stopped by the parser",
161                                  cp->lval);
162             }
163 
164             if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILFROM].lval) == 0)
165             {
166                 free(exec_config->mail_from_address);
167                 exec_config->mail_from_address = xstrdup(value);
168                 Log(LOG_LEVEL_DEBUG, "mailfrom '%s'", exec_config->mail_from_address);
169             }
170             else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILTO].lval) == 0)
171             {
172                 free(exec_config->mail_to_address);
173                 exec_config->mail_to_address = xstrdup(value);
174                 Log(LOG_LEVEL_DEBUG, "mailto '%s'", exec_config->mail_to_address);
175             }
176             else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILSUBJECT].lval) == 0)
177             {
178                 free(exec_config->mail_subject);
179                 exec_config->mail_subject = xstrdup(value);
180                 Log(LOG_LEVEL_DEBUG, "mailsubject '%s'", exec_config->mail_subject);
181             }
182             else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_SMTPSERVER].lval) == 0)
183             {
184                 free(exec_config->mail_server);
185                 exec_config->mail_server = xstrdup(value);
186                 Log(LOG_LEVEL_DEBUG, "smtpserver '%s'", exec_config->mail_server);
187             }
188             else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_EXECCOMMAND].lval) == 0)
189             {
190                 free(exec_config->exec_command);
191                 exec_config->exec_command = xstrdup(value);
192                 Log(LOG_LEVEL_DEBUG, "exec_command '%s'", exec_config->exec_command);
193             }
194             else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_AGENT_EXPIREAFTER].lval) == 0)
195             {
196                 exec_config->agent_expireafter = IntFromString(value);
197                 Log(LOG_LEVEL_DEBUG, "agent_expireafter %d", exec_config->agent_expireafter);
198             }
199             else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILMAXLINES].lval) == 0)
200             {
201                 exec_config->mail_max_lines = IntFromString(value);
202                 Log(LOG_LEVEL_DEBUG, "maxlines %d", exec_config->mail_max_lines);
203             }
204             else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILFILTER_INCLUDE].lval) == 0)
205             {
206                 SeqDestroy(exec_config->mailfilter_include);
207                 SeqDestroy(exec_config->mailfilter_include_regex);
208                 RlistMailFilterFill(value, &exec_config->mailfilter_include,
209                                     &exec_config->mailfilter_include_regex, "include");
210             }
211             else if (strcmp(cp->lval, CFEX_CONTROLBODY[EXEC_CONTROL_MAILFILTER_EXCLUDE].lval) == 0)
212             {
213                 SeqDestroy(exec_config->mailfilter_exclude);
214                 SeqDestroy(exec_config->mailfilter_exclude_regex);
215                 RlistMailFilterFill(value, &exec_config->mailfilter_exclude,
216                                     &exec_config->mailfilter_exclude_regex, "exclude");
217             }
218         }
219     }
220 
221     return exec_config;
222 }
223 
ExecConfigCopy(const ExecConfig * config)224 ExecConfig *ExecConfigCopy(const ExecConfig *config)
225 {
226     ExecConfig *copy = xcalloc(1, sizeof(ExecConfig));
227 
228     copy->scheduled_run = config->scheduled_run;
229     copy->exec_command = xstrdup(config->exec_command);
230     copy->agent_expireafter = config->agent_expireafter;
231     copy->mail_server = xstrdup(config->mail_server);
232     copy->mail_from_address = xstrdup(config->mail_from_address);
233     copy->mail_to_address = xstrdup(config->mail_to_address);
234     copy->mail_subject = xstrdup(config->mail_subject);
235     copy->mail_max_lines = config->mail_max_lines;
236     SeqMailFilterFill(config->mailfilter_include, &copy->mailfilter_include,
237                       &copy->mailfilter_include_regex, "include");
238     SeqMailFilterFill(config->mailfilter_exclude, &copy->mailfilter_exclude,
239                       &copy->mailfilter_exclude_regex, "exclude");
240     copy->fq_name = xstrdup(config->fq_name);
241     copy->ip_address = xstrdup(config->ip_address);
242     copy->ip_addresses = xstrdup(config->ip_addresses);
243 
244     return copy;
245 }
246 
ExecConfigDestroy(ExecConfig * exec_config)247 void ExecConfigDestroy(ExecConfig *exec_config)
248 {
249     if (exec_config)
250     {
251         free(exec_config->exec_command);
252         free(exec_config->mail_server);
253         free(exec_config->mail_from_address);
254         free(exec_config->mail_to_address);
255         free(exec_config->mail_subject);
256         SeqDestroy(exec_config->mailfilter_include);
257         SeqDestroy(exec_config->mailfilter_exclude);
258         SeqDestroy(exec_config->mailfilter_include_regex);
259         SeqDestroy(exec_config->mailfilter_exclude_regex);
260         free(exec_config->fq_name);
261         free(exec_config->ip_address);
262         free(exec_config->ip_addresses);
263 
264         free(exec_config);
265     }
266 }
267