write audit log call variant which accepts json objects; old/new value logging for...
[public/netxms.git] / src / server / core / audit.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: audit.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Static data
27 */
28 static VolatileCounter m_recordId = 1;
29 static InetAddress m_auditServerAddr;
30 static WORD m_auditServerPort;
31 static int m_auditFacility;
32 static int m_auditSeverity;
33 static char m_auditTag[MAX_SYSLOG_TAG_LEN];
34 static char m_localHostName[256];
35
36 /**
37 * Send syslog record to audit server
38 */
39 static void SendSyslogRecord(const TCHAR *text)
40 {
41 static char month[12][5] = { "Jan ", "Feb ", "Mar ", "Apr ",
42 "May ", "Jun ", "Jul ", "Aug ",
43 "Sep ", "Oct ", "Nov ", "Dec " };
44 if (!m_auditServerAddr.isValidUnicast())
45 return;
46
47 time_t ts = time(NULL);
48 struct tm *now = localtime(&ts);
49
50 char message[1025];
51 #ifdef UNICODE
52 char *mbText = MBStringFromWideString(text);
53 snprintf(message, 1025, "<%d>%s %2d %02d:%02d:%02d %s %s %s", (m_auditFacility << 3) + m_auditSeverity, month[now->tm_mon],
54 now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec, m_localHostName, m_auditTag, mbText);
55 free(mbText);
56 #else
57 snprintf(message, 1025, "<%d>%s %2d %02d:%02d:%02d %s %s %s", (m_auditFacility << 3) + m_auditSeverity, month[now->tm_mon],
58 now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec, m_localHostName, m_auditTag, text);
59 #endif
60 message[1024] = 0;
61
62 SOCKET hSocket = socket(m_auditServerAddr.getFamily(), SOCK_DGRAM, 0);
63 if (hSocket != INVALID_SOCKET)
64 {
65 SockAddrBuffer addr;
66 m_auditServerAddr.fillSockAddr(&addr, m_auditServerPort);
67 sendto(hSocket, message, (int)strlen(message), 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
68 shutdown(hSocket, SHUT_RDWR);
69 closesocket(hSocket);
70 }
71 }
72
73 /**
74 * Initalize audit log
75 */
76 void InitAuditLog()
77 {
78 DB_RESULT hResult;
79
80 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
81 hResult = DBSelect(hdb, _T("SELECT max(record_id) FROM audit_log"));
82 if (hResult != NULL)
83 {
84 if (DBGetNumRows(hResult) > 0)
85 m_recordId = DBGetFieldULong(hResult, 0, 0) + 1;
86 DBFreeResult(hResult);
87 }
88
89 // External audit server
90 TCHAR temp[256];
91 ConfigReadStr(_T("ExternalAuditServer"), temp, 256, _T("none"));
92 if (_tcscmp(temp, _T("none")))
93 {
94 m_auditServerAddr = InetAddress::resolveHostName(temp);
95 m_auditServerPort = (WORD)ConfigReadInt(_T("ExternalAuditPort"), 514);
96 m_auditFacility = ConfigReadInt(_T("ExternalAuditFacility"), 13); // default is log audit facility
97 m_auditSeverity = ConfigReadInt(_T("ExternalAuditSeverity"), SYSLOG_SEVERITY_NOTICE);
98 ConfigReadStrA(_T("ExternalAuditTag"), m_auditTag, MAX_SYSLOG_TAG_LEN, "netxmsd-audit");
99
100 // Get local host name
101 #ifdef _WIN32
102 DWORD size = 256;
103 GetComputerNameA(m_localHostName, &size);
104 #else
105 gethostname(m_localHostName, 256);
106 m_localHostName[255] = 0;
107 char *ptr = strchr(m_localHostName, '.');
108 if (ptr != NULL)
109 *ptr = 0;
110 #endif
111
112 SendSyslogRecord(_T("NetXMS server audit subsystem started"));
113 }
114 DBConnectionPoolReleaseConnection(hdb);
115 }
116
117 /**
118 * Handler for EnumerateSessions()
119 */
120 static void SendNewRecord(ClientSession *session, void *arg)
121 {
122 if (!session->isAuthenticated() || !session->isSubscribedTo(NXC_CHANNEL_AUDIT_LOG))
123 return;
124
125 UPDATE_INFO *pUpdate = (UPDATE_INFO *)malloc(sizeof(UPDATE_INFO));
126 pUpdate->dwCategory = INFO_CAT_AUDIT_RECORD;
127 pUpdate->pData = new NXCPMessage((NXCPMessage *)arg);
128 session->queueUpdate(pUpdate);
129 }
130
131 /**
132 * Write audit record
133 */
134 void NXCORE_EXPORTABLE WriteAuditLog(const TCHAR *subsys, bool isSuccess, UINT32 userId,
135 const TCHAR *workstation, int sessionId, UINT32 objectId,
136 const TCHAR *format, ...)
137 {
138 va_list args;
139 va_start(args, format);
140 WriteAuditLogWithValues2(subsys, isSuccess, userId, workstation, sessionId, objectId, NULL, NULL, format, args);
141 va_end(args);
142 }
143
144 /**
145 * Write audit record
146 */
147 void NXCORE_EXPORTABLE WriteAuditLog2(const TCHAR *subsys, bool isSuccess, UINT32 userId,
148 const TCHAR *workstation, int sessionId, UINT32 objectId,
149 const TCHAR *format, va_list args)
150 {
151 WriteAuditLogWithValues2(subsys, isSuccess, userId, workstation, sessionId, objectId, NULL, NULL, format, args);
152 }
153
154 /**
155 * Write audit record with old and new values
156 */
157 void NXCORE_EXPORTABLE WriteAuditLogWithValues(const TCHAR *subsys, bool isSuccess, UINT32 userId,
158 const TCHAR *workstation, int sessionId, UINT32 objectId,
159 const TCHAR *oldValue, const TCHAR *newValue,
160 const TCHAR *format, ...)
161 {
162 va_list args;
163 va_start(args, format);
164 WriteAuditLogWithValues2(subsys, isSuccess, userId, workstation, sessionId, objectId, oldValue, newValue, format, args);
165 va_end(args);
166 }
167
168 /**
169 * Write audit record with old and new values in JSON format
170 */
171 void NXCORE_EXPORTABLE WriteAuditLogWithJsonValues(const TCHAR *subsys, bool isSuccess, UINT32 userId,
172 const TCHAR *workstation, int sessionId, UINT32 objectId,
173 json_t *oldValue, json_t *newValue,
174 const TCHAR *format, ...)
175 {
176 va_list args;
177 va_start(args, format);
178 WriteAuditLogWithJsonValues2(subsys, isSuccess, userId, workstation, sessionId, objectId, oldValue, newValue, format, args);
179 va_end(args);
180 }
181
182 /**
183 * Write audit record with old and new values
184 */
185 void NXCORE_EXPORTABLE WriteAuditLogWithValues2(const TCHAR *subsys, bool isSuccess, UINT32 userId,
186 const TCHAR *workstation, int sessionId, UINT32 objectId,
187 const TCHAR *oldValue, const TCHAR *newValue,
188 const TCHAR *format, va_list args)
189 {
190 String text;
191 text.appendFormattedStringV(format, args);
192
193 TCHAR recordId[16], _time[32], success[2], _userId[16], _sessionId[16], _objectId[16];
194 const TCHAR *values[11] = { recordId, _time, subsys, success, _userId, workstation, _sessionId, _objectId, (const TCHAR *)text, oldValue, newValue };
195 _sntprintf(recordId, 16, _T("%d"), InterlockedIncrement(&m_recordId));
196 _sntprintf(_time, 32, _T("%d"), (UINT32)time(NULL));
197 _sntprintf(success, 2, _T("%d"), isSuccess);
198 _sntprintf(_userId, 16, _T("%d"), userId);
199 _sntprintf(_sessionId, 16, _T("%d"), sessionId);
200 _sntprintf(_objectId, 16, _T("%d"), objectId);
201
202 static int sqlTypes[11] = { DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER, DB_SQLTYPE_VARCHAR, DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER, DB_SQLTYPE_VARCHAR, DB_SQLTYPE_INTEGER, DB_SQLTYPE_INTEGER, DB_SQLTYPE_TEXT, DB_SQLTYPE_TEXT, DB_SQLTYPE_TEXT };
203 if ((oldValue != NULL) && (newValue != NULL))
204 QueueSQLRequest(_T("INSERT INTO audit_log (record_id,timestamp,subsystem,success,user_id,workstation,session_id,object_id,message,old_value,new_value) VALUES (?,?,?,?,?,?,?,?,?,?,?)"), 11, sqlTypes, values);
205 else
206 QueueSQLRequest(_T("INSERT INTO audit_log (record_id,timestamp,subsystem,success,user_id,workstation,session_id,object_id,message) VALUES (?,?,?,?,?,?,?,?,?)"), 9, sqlTypes, values);
207
208 NXCPMessage msg;
209 msg.setCode(CMD_AUDIT_RECORD);
210 msg.setField(VID_SUBSYSTEM, subsys);
211 msg.setField(VID_SUCCESS_AUDIT, (WORD)isSuccess);
212 msg.setField(VID_USER_ID, userId);
213 msg.setField(VID_WORKSTATION, workstation);
214 msg.setField(VID_SESSION_ID, sessionId);
215 msg.setField(VID_OBJECT_ID, objectId);
216 msg.setField(VID_MESSAGE, (const TCHAR *)text);
217 EnumerateClientSessions(SendNewRecord, &msg);
218
219 if (m_auditServerAddr.isValidUnicast())
220 {
221 String extText;
222 TCHAR buffer[256];
223
224 extText = _T("[");
225 if (ResolveUserId(userId, buffer, 256))
226 {
227 extText += buffer;
228 }
229 else
230 {
231 extText.appendFormattedString(_T("{%d}"), userId);
232 }
233
234 extText.appendFormattedString(_T("@%s] "), workstation);
235
236 extText += (const TCHAR *)text;
237 SendSyslogRecord((const TCHAR *)extText);
238 }
239 }
240
241 /**
242 * Write audit record with old and new values
243 */
244 void NXCORE_EXPORTABLE WriteAuditLogWithJsonValues2(const TCHAR *subsys, bool isSuccess, UINT32 userId,
245 const TCHAR *workstation, int sessionId, UINT32 objectId,
246 json_t *oldValue, json_t *newValue,
247 const TCHAR *format, va_list args)
248 {
249 char *js1 = (oldValue != NULL) ? json_dumps(oldValue, 0) : strdup("");
250 char *js2 = (newValue != NULL) ? json_dumps(newValue, 0) : strdup("");
251 #ifdef UNICODE
252 WCHAR *js1w = WideStringFromUTF8String(js1);
253 WCHAR *js2w = WideStringFromUTF8String(js2);
254 WriteAuditLogWithValues2(subsys, isSuccess, userId, workstation, sessionId, objectId, js1w, js2w, format, args);
255 free(js1w);
256 free(js2w);
257 #else
258 WriteAuditLogWithValues2(subsys, isSuccess, userId, workstation, sessionId, objectId, js1, js2, format, args);
259 #endif
260 free(js1);
261 free(js2);
262 }