1 /*
2   Copyright 2020 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 <agent-diagnostics.h>
26 
27 #include <alloc.h>
28 #include <crypto.h>
29 #include <files_interfaces.h>
30 #include <string_lib.h>
31 #include <bootstrap.h>
32 #include <policy_server.h>
33 #include <dbm_api.h>
34 #include <dbm_priv.h>
35 #include <tokyo_check.h>
36 #include <lastseen.h>
37 #include <file_lib.h>
38 #include <known_dirs.h>
39 
40 
AgentDiagnosticsResultNew(bool success,char * message)41 AgentDiagnosticsResult AgentDiagnosticsResultNew(bool success, char *message)
42 {
43     return (AgentDiagnosticsResult) { success, message };
44 }
45 
AgentDiagnosticsResultDestroy(AgentDiagnosticsResult result)46 static void AgentDiagnosticsResultDestroy(AgentDiagnosticsResult result)
47 {
48     free(result.message);
49 }
50 
AgentDiagnosticsRun(const char * workdir,const AgentDiagnosticCheck checks[],Writer * output)51 void AgentDiagnosticsRun(const char *workdir, const AgentDiagnosticCheck checks[], Writer *output)
52 {
53     {
54         char diagnostics_path[CF_BUFSIZE] = { 0 };
55         snprintf(diagnostics_path, CF_BUFSIZE, "%s/diagnostics", workdir);
56         MapName(diagnostics_path);
57 
58         struct stat sb;
59         if (stat(diagnostics_path, &sb) != 0)
60         {
61             if (mkdir(diagnostics_path, DEFAULTMODE) != 0)
62             {
63                 WriterWriteF(output, "Cannot create diagnostics output directory '%s'", diagnostics_path);
64                 return;
65             }
66         }
67     }
68 
69 
70     for (int i = 0; checks[i].description; i++)
71     {
72         AgentDiagnosticsResult result = checks[i].check(workdir);
73         WriterWriteF(output, "[ %s ] %s: %s\n",
74                      result.success ? "YES" : "NO ",
75                      checks[i].description,
76                      result.message);
77         AgentDiagnosticsResultDestroy(result);
78     }
79 }
80 
AgentDiagnosticsCheckIsBootstrapped(const char * workdir)81 AgentDiagnosticsResult AgentDiagnosticsCheckIsBootstrapped(const char *workdir)
82 {
83     char *policy_server = PolicyServerReadFile(workdir);
84     return AgentDiagnosticsResultNew(policy_server != NULL,
85                                      policy_server != NULL ? policy_server : xstrdup("Not bootstrapped"));
86 }
87 
AgentDiagnosticsCheckAmPolicyServer(ARG_UNUSED const char * workdir)88 AgentDiagnosticsResult AgentDiagnosticsCheckAmPolicyServer(ARG_UNUSED const char *workdir)
89 {
90     bool am_policy_server = GetAmPolicyHub();
91     return AgentDiagnosticsResultNew(am_policy_server,
92                                      am_policy_server ? xstrdup("Acting as a policy server") : xstrdup("Not acting as a policy server"));
93 }
94 
AgentDiagnosticsCheckPrivateKey(const char * workdir)95 AgentDiagnosticsResult AgentDiagnosticsCheckPrivateKey(const char *workdir)
96 {
97     char *path = PrivateKeyFile(workdir);
98     struct stat sb;
99     AgentDiagnosticsResult res;
100 
101     if (stat(path, &sb) != 0)
102     {
103         res = AgentDiagnosticsResultNew(false, StringFormat("No private key found at '%s'", path));
104     }
105     else if (sb.st_mode != (S_IFREG | S_IWUSR | S_IRUSR))
106     {
107         res = AgentDiagnosticsResultNew(false, StringFormat("Private key found at '%s', but had incorrect permissions '%o'", path, sb.st_mode));
108     }
109     else
110     {
111         res = AgentDiagnosticsResultNew(true, StringFormat("OK at '%s'", path));
112     }
113 
114     free(path);
115     return res;
116 }
117 
AgentDiagnosticsCheckPublicKey(const char * workdir)118 AgentDiagnosticsResult AgentDiagnosticsCheckPublicKey(const char *workdir)
119 {
120     char *path = PublicKeyFile(workdir);
121     struct stat sb;
122     AgentDiagnosticsResult res;
123 
124     if (stat(path, &sb) != 0)
125     {
126         res = AgentDiagnosticsResultNew(false, StringFormat("No public key found at '%s'", path));
127     }
128     else if (sb.st_mode != (S_IFREG | S_IWUSR | S_IRUSR))
129     {
130         res = AgentDiagnosticsResultNew(false, StringFormat("Public key found at '%s', but had incorrect permissions '%o'", path, sb.st_mode));
131     }
132     else if (sb.st_size != 426)
133     {
134         res = AgentDiagnosticsResultNew(false, StringFormat("Public key at '%s' had size %lld bytes, expected 426 bytes", path, (long long)sb.st_size));
135     }
136     else
137     {
138         res = AgentDiagnosticsResultNew(true, StringFormat("OK at '%s'", path));
139     }
140 
141     free(path);
142     return res;
143 }
144 
AgentDiagnosticsCheckDB(ARG_UNUSED const char * workdir,dbid id)145 static AgentDiagnosticsResult AgentDiagnosticsCheckDB(ARG_UNUSED const char *workdir, dbid id)
146 {
147     char *dbpath = DBIdToPath(id);
148     char *error = DBPrivDiagnose(dbpath);
149 
150     if (error)
151     {
152         free(dbpath);
153         return AgentDiagnosticsResultNew(false, error);
154     }
155     else
156     {
157         int ret = CheckTokyoDBCoherence(dbpath);
158         free(dbpath);
159         if (ret)
160         {
161             return AgentDiagnosticsResultNew(false, xstrdup("Internal DB coherence problem"));
162         }
163         else
164         {
165             if (id == dbid_lastseen)
166             {
167                 if (IsLastSeenCoherent() == false)
168                 {
169                     return AgentDiagnosticsResultNew(false, xstrdup("Lastseen DB data coherence problem"));
170                 }
171             }
172             return AgentDiagnosticsResultNew(true, xstrdup("OK"));
173 
174         }
175     }
176 }
177 
AgentDiagnosticsCheckDBPersistentClasses(const char * workdir)178 AgentDiagnosticsResult AgentDiagnosticsCheckDBPersistentClasses(const char *workdir)
179 {
180     return AgentDiagnosticsCheckDB(workdir, dbid_state);
181 }
182 
AgentDiagnosticsCheckDBChecksums(const char * workdir)183 AgentDiagnosticsResult AgentDiagnosticsCheckDBChecksums(const char *workdir)
184 {
185     return AgentDiagnosticsCheckDB(workdir, dbid_checksums);
186 }
187 
AgentDiagnosticsCheckDBLastSeen(const char * workdir)188 AgentDiagnosticsResult AgentDiagnosticsCheckDBLastSeen(const char *workdir)
189 {
190     return AgentDiagnosticsCheckDB(workdir, dbid_lastseen);
191 }
192 
AgentDiagnosticsCheckDBObservations(const char * workdir)193 AgentDiagnosticsResult AgentDiagnosticsCheckDBObservations(const char *workdir)
194 {
195     return AgentDiagnosticsCheckDB(workdir, dbid_observations);
196 }
197 
AgentDiagnosticsCheckDBFileStats(const char * workdir)198 AgentDiagnosticsResult AgentDiagnosticsCheckDBFileStats(const char *workdir)
199 {
200     return AgentDiagnosticsCheckDB(workdir, dbid_filestats);
201 }
202 
AgentDiagnosticsCheckDBLocks(const char * workdir)203 AgentDiagnosticsResult AgentDiagnosticsCheckDBLocks(const char *workdir)
204 {
205     return AgentDiagnosticsCheckDB(workdir, dbid_locks);
206 }
207 
AgentDiagnosticsCheckDBPerformance(const char * workdir)208 AgentDiagnosticsResult AgentDiagnosticsCheckDBPerformance(const char *workdir)
209 {
210     return AgentDiagnosticsCheckDB(workdir, dbid_performance);
211 }
212 
AgentDiagnosticsAllChecks(void)213 const AgentDiagnosticCheck *AgentDiagnosticsAllChecks(void)
214 {
215     static const AgentDiagnosticCheck checks[] =
216     {
217         { "Check that agent is bootstrapped", &AgentDiagnosticsCheckIsBootstrapped },
218         { "Check if agent is acting as a policy server", &AgentDiagnosticsCheckAmPolicyServer },
219         { "Check private key", &AgentDiagnosticsCheckPrivateKey },
220         { "Check public key", &AgentDiagnosticsCheckPublicKey },
221 
222         { "Check persistent classes DB", &AgentDiagnosticsCheckDBPersistentClasses },
223         { "Check checksums DB", &AgentDiagnosticsCheckDBChecksums },
224         { "Check observations DB", &AgentDiagnosticsCheckDBObservations },
225         { "Check file stats DB", &AgentDiagnosticsCheckDBFileStats },
226         { "Check locks DB", &AgentDiagnosticsCheckDBLocks },
227         { "Check performance DB", &AgentDiagnosticsCheckDBPerformance },
228         { "Check lastseen DB", &AgentDiagnosticsCheckDBLastSeen },
229         { NULL, NULL }
230     };
231 
232     return checks;
233 }
234 
ENTERPRISE_VOID_FUNC_4ARG_DEFINE_STUB(void,AgentDiagnosticsRunAllChecksNova,ARG_UNUSED const char *,workdir,ARG_UNUSED Writer *,output,ARG_UNUSED AgentDiagnosticsRunFunction,AgentDiagnosticsRunPtr,ARG_UNUSED AgentDiagnosticsResultNewFunction,AgentDiagnosticsResultNewPtr)235 ENTERPRISE_VOID_FUNC_4ARG_DEFINE_STUB(void, AgentDiagnosticsRunAllChecksNova,
236                                       ARG_UNUSED const char *, workdir, ARG_UNUSED Writer *, output,
237                                       ARG_UNUSED AgentDiagnosticsRunFunction, AgentDiagnosticsRunPtr,
238                                       ARG_UNUSED AgentDiagnosticsResultNewFunction, AgentDiagnosticsResultNewPtr)
239 {
240 }
241