dsdb: audit samdb and password changes
[nivanova/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / audit_util.c
1 /*
2    ldb database module utility library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21  * Common utility functions for SamDb audit logging.
22  *
23  */
24
25 #include "includes.h"
26 #include "ldb_module.h"
27 #include "lib/audit_logging/audit_logging.h"
28
29 #include "dsdb/samdb/samdb.h"
30 #include "dsdb/samdb/ldb_modules/util.h"
31 #include "libcli/security/dom_sid.h"
32 #include "libcli/security/security_token.h"
33 #include "auth/common_auth.h"
34 #include "param/param.h"
35 #include "dsdb/samdb/ldb_modules/util.h"
36
37 #define MAX_LENGTH 1024
38
39 #define min(a, b) (((a)>(b))?(b):(a))
40
41 /*
42  * List of attributes considered secret or confidential the values of these
43  * attributes should not be displayed in log messages.
44  */
45 static const char * const secret_attributes[] = {
46         DSDB_SECRET_ATTRIBUTES,
47         NULL};
48 /*
49  * List of attributes that contain a password, used to detect password changes
50  */
51 static const char * const password_attributes[] = {
52         DSDB_PASSWORD_ATTRIBUTES,
53         NULL};
54
55 /*
56  * @brief Should the value of the specified value be redacted.
57  *
58  * The values of secret or password attributes should not be displayed.
59  *
60  * @param name The attributes name.
61  *
62  * @return True if the attribute should be redacted
63  */
64 bool dsdb_audit_redact_attribute(const char * name)
65 {
66
67         if (ldb_attr_in_list(secret_attributes, name)) {
68                 return true;
69         }
70
71         if (ldb_attr_in_list(password_attributes, name)) {
72                 return true;
73         }
74
75         return false;
76 }
77
78 /*
79  * @brief is the attribute a password attribute?
80  *
81  * Is the attribute a password attribute.
82  *
83  * @return True if the attribute is a "Password" attribute.
84  */
85 bool dsdb_audit_is_password_attribute(const char * name)
86 {
87
88         bool is_password = ldb_attr_in_list(password_attributes, name);
89         return is_password;
90 }
91
92 /*
93  * @brief Get the remote address from the ldb context.
94  *
95  * The remote address is stored in the ldb opaque value "remoteAddress"
96  * it is the responsibility of the higher level code to ensure that this
97  * value is set.
98  *
99  * @param ldb the ldb_context.
100  *
101  * @return the remote address if known, otherwise NULL.
102  */
103 const struct tsocket_address *dsdb_audit_get_remote_address(
104         struct ldb_context *ldb)
105 {
106         void *opaque_remote_address = NULL;
107         struct tsocket_address *remote_address;
108
109         opaque_remote_address = ldb_get_opaque(ldb,
110                                                "remoteAddress");
111         if (opaque_remote_address == NULL) {
112                 return NULL;
113         }
114
115         remote_address = talloc_get_type(opaque_remote_address,
116                                          struct tsocket_address);
117         return remote_address;
118 }
119
120 /*
121  * @brief Get the actual user SID from ldb context.
122  *
123  * The actual user SID is stored in the ldb opaque value "networkSessionInfo"
124  * it is the responsibility of the higher level code to ensure that this
125  * value is set.
126  *
127  * @param ldb the ldb_context.
128  *
129  * @return the users actual sid.
130  */
131 const struct dom_sid *dsdb_audit_get_actual_sid(struct ldb_context *ldb)
132 {
133         void *opaque_session = NULL;
134         struct auth_session_info *session = NULL;
135         struct security_token *user_token = NULL;
136
137         opaque_session = ldb_get_opaque(ldb, "networkSessionInfo");
138         if (opaque_session == NULL) {
139                 return NULL;
140         }
141
142         session = talloc_get_type(opaque_session, struct auth_session_info);
143         if (session == NULL) {
144                 return NULL;
145         }
146
147         user_token = session->security_token;
148         if (user_token == NULL) {
149                 return NULL;
150         }
151         return &user_token->sids[0];
152 }
153 /*
154  * @brief get the ldb error string.
155  *
156  * Get the ldb error string if set, otherwise get the generic error code
157  * for the status code.
158  *
159  * @param ldb the ldb_context.
160  * @param status the ldb_status code.
161  *
162  * @return a string describing the error.
163  */
164 const char *dsdb_audit_get_ldb_error_string(
165         struct ldb_module *module,
166         int status)
167 {
168         struct ldb_context *ldb = ldb_module_get_ctx(module);
169         const char *err_string = ldb_errstring(ldb);
170
171         if (err_string == NULL) {
172                 return ldb_strerror(status);
173         }
174         return err_string;
175 }
176
177 /*
178  * @brief get the SID of the user performing the operation.
179  *
180  * Get the SID of the user performing the operation.
181  *
182  * @param module the ldb_module.
183  *
184  * @return the SID of the currently logged on user.
185  */
186 const struct dom_sid *dsdb_audit_get_user_sid(const struct ldb_module *module)
187 {
188         struct security_token *user_token = NULL;
189
190         /*
191          * acl_user_token does not alter module so it's safe
192          * to discard the const.
193          */
194         user_token = acl_user_token(discard_const(module));
195         if (user_token == NULL) {
196                 return NULL;
197         }
198         return &user_token->sids[0];
199
200 }
201
202 /*
203  * @brief is operation being performed using the system session.
204  *
205  * Is the operation being performed using the system session.
206  *
207  * @param module the ldb_module.
208  *
209  * @return true if the operation is being performed using the system session.
210  */
211 bool dsdb_audit_is_system_session(const struct ldb_module *module)
212 {
213         struct security_token *user_token = NULL;
214
215         /*
216          * acl_user_token does not alter module and security_token_is_system
217          * does not alter the security token so it's safe to discard the const.
218          */
219         user_token = acl_user_token(discard_const(module));
220         if (user_token == NULL) {
221                 return false;
222         }
223         return security_token_is_system(user_token);;
224
225 }
226
227 /*
228  * @brief get the session identifier GUID
229  *
230  * Get the GUID that uniquely identifies the current authenticated session.
231  *
232  * @param module the ldb_module.
233  *
234  * @return the unique session GUID
235  */
236 const struct GUID *dsdb_audit_get_unique_session_token(
237         const struct ldb_module *module)
238 {
239         struct ldb_context *ldb = ldb_module_get_ctx(discard_const(module));
240         struct auth_session_info *session_info
241                 = (struct auth_session_info *)ldb_get_opaque(
242                         ldb,
243                         "sessionInfo");
244         if(!session_info) {
245                 return NULL;
246         }
247         return &session_info->unique_session_token;
248 }
249
250 /*
251  * @brief get the actual user session identifier
252  *
253  * Get the GUID that uniquely identifies the current authenticated session.
254  * This is the session of the connected user, as it may differ from the
255  * session the operation is being performed as, i.e. for operations performed
256  * under the system session.
257  *
258  * @param context the ldb_context.
259  *
260  * @return the unique session GUID
261  */
262 const struct GUID *dsdb_audit_get_actual_unique_session_token(
263         struct ldb_context *ldb)
264 {
265         struct auth_session_info *session_info
266                 = (struct auth_session_info *)ldb_get_opaque(
267                         ldb,
268                         "networkSessionInfo");
269         if(!session_info) {
270                 return NULL;
271         }
272         return &session_info->unique_session_token;
273 }
274
275 /*
276  * @brief Get a printable string value for the remote host address.
277  *
278  * Get a printable string representation of the remote host, for display in the
279  * the audit logs.
280  *
281  * @param ldb the ldb context.
282  * @param mem_ctx the talloc memory context that will own the returned string.
283  *
284  * @return A string representation of the remote host address or "Unknown"
285  *
286  */
287 char *dsdb_audit_get_remote_host(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
288 {
289         const struct tsocket_address *remote_address;
290         char* remote_host = NULL;
291
292         remote_address = dsdb_audit_get_remote_address(ldb);
293         if (remote_address == NULL) {
294                 remote_host = talloc_asprintf(mem_ctx, "Unknown");
295                 return remote_host;
296         }
297
298         remote_host = tsocket_address_string(remote_address, mem_ctx);
299         return remote_host;
300 }
301
302 /*
303  * @brief get a printable representation of the primary DN.
304  *
305  * Get a printable representation of the primary DN. The primary DN is the
306  * DN of the object being added, deleted, modified or renamed.
307  *
308  * @param the ldb_request.
309  *
310  * @return a printable and linearized DN
311  */
312 const char* dsdb_audit_get_primary_dn(const struct ldb_request *request)
313 {
314         struct ldb_dn *dn = NULL;
315         switch (request->operation) {
316         case LDB_ADD:
317                 if (request->op.add.message != NULL) {
318                         dn = request->op.add.message->dn;
319                 }
320                 break;
321         case LDB_MODIFY:
322                 if (request->op.mod.message != NULL) {
323                         dn = request->op.mod.message->dn;
324                 }
325                 break;
326         case LDB_DELETE:
327                 dn = request->op.del.dn;
328                 break;
329         case LDB_RENAME:
330                 dn = request->op.rename.olddn;
331                 break;
332         default:
333                 dn = NULL;
334                 break;
335         }
336         if (dn == NULL) {
337                 return NULL;
338         }
339         return ldb_dn_get_linearized(dn);
340 }
341
342 /*
343  * @brief Get the ldb_message from a request.
344  *
345  * Get the ldb_message for the request, returns NULL is there is no
346  * associated ldb_message
347  *
348  * @param The request
349  *
350  * @return the message associated with this request, or NULL
351  */
352 const struct ldb_message *dsdb_audit_get_message(
353         const struct ldb_request *request)
354 {
355         switch (request->operation) {
356         case LDB_ADD:
357                 return request->op.add.message;
358         case LDB_MODIFY:
359                 return request->op.mod.message;
360         default:
361                 return NULL;
362         }
363 }
364
365 /*
366  * @brief get the secondary dn, i.e. the target dn for a rename.
367  *
368  * Get the secondary dn, i.e. the target for a rename. This is only applicable
369  * got a rename operation, for the non rename operations this function returns
370  * NULL.
371  *
372  * @param request the ldb_request.
373  *
374  * @return the secondary dn in a printable and linearized form.
375  */
376 const char *dsdb_audit_get_secondary_dn(const struct ldb_request *request)
377 {
378         switch (request->operation) {
379         case LDB_RENAME:
380                 return ldb_dn_get_linearized(request->op.rename.newdn);
381         default:
382                 return NULL;
383         }
384 }
385
386 /*
387  * @brief Map the request operation to a description.
388  *
389  * Get a description of the operation for logging
390  *
391  * @param request the ldb_request
392  *
393  * @return a string describing the operation, or "Unknown" if the operation
394  *         is not known.
395  */
396 const char *dsdb_audit_get_operation_name(const struct ldb_request *request)
397 {
398         switch (request->operation) {
399         case LDB_SEARCH:
400                 return "Search";
401         case LDB_ADD:
402                 return "Add";
403         case LDB_MODIFY:
404                 return "Modify";
405         case LDB_DELETE:
406                 return "Delete";
407         case LDB_RENAME:
408                 return "Rename";
409         case LDB_EXTENDED:
410                 return "Extended";
411         case LDB_REQ_REGISTER_CONTROL:
412                 return "Register Control";
413         case LDB_REQ_REGISTER_PARTITION:
414                 return "Register Partition";
415         default:
416                 return "Unknown";
417         }
418 }
419
420 /*
421  * @brief get a description of a modify action for logging.
422  *
423  * Get a brief description of the modification action suitable for logging.
424  *
425  * @param flags the ldb_attributes flags.
426  *
427  * @return a brief description, or "unknown".
428  */
429 const char *dsdb_audit_get_modification_action(unsigned int flags)
430 {
431         switch (LDB_FLAG_MOD_TYPE(flags)) {
432         case LDB_FLAG_MOD_ADD:
433                 return "add";
434         case LDB_FLAG_MOD_DELETE:
435                 return "delete";
436         case LDB_FLAG_MOD_REPLACE:
437                 return "replace";
438         default:
439                 return "unknown";
440         }
441 }
442
443 /*
444  * @brief Add an ldb_value to a json object array
445  *
446  * Convert the current ldb_value to a JSON object and append it to array.
447  * {
448  *      "value":"xxxxxxxx",
449  *      "base64":true
450  *      "truncated":true
451  * }
452  *
453  * value     is the JSON string representation of the ldb_val,
454  *           will be null if the value is zero length. The value will be
455  *           truncated if it is more than MAX_LENGTH bytes long. It will also
456  *           be base64 encoded if it contains any non printable characters.
457  *
458  * base64    Indicates that the value is base64 encoded, will be absent if the
459  *           value is not encoded.
460  *
461  * truncated Indicates that the length of the value exceeded MAX_LENGTH and was
462  *           truncated.  Note that vales are truncated and then base64 encoded.
463  *           so an encoded value can be longer than MAX_LENGTH.
464  *
465  * @param array the JSON array to append the value to.
466  * @param lv the ldb_val to convert and append to the array.
467  *
468  */
469 static void dsdb_audit_add_ldb_value(
470         struct json_object *array,
471         const struct ldb_val lv)
472 {
473
474         json_assert_is_array(array);
475         if (json_is_invalid(array)) {
476                 return;
477         }
478
479         if (lv.length == 0 || lv.data == NULL) {
480                 json_add_object(array, NULL, NULL);
481                 return;
482         }
483
484         bool base64 = ldb_should_b64_encode(NULL, &lv);
485         int len = min(lv.length, MAX_LENGTH);
486         struct json_object value = json_new_object();
487         if (lv.length > MAX_LENGTH) {
488                 json_add_bool(&value, "truncated", true);
489         }
490         if (base64) {
491                 TALLOC_CTX *ctx = talloc_new(NULL);
492                 char *encoded = ldb_base64_encode(
493                         ctx,
494                         (char*) lv.data,
495                         len);
496
497                 json_add_bool(&value, "base64", true);
498                 json_add_string(&value, "value", encoded);
499                 TALLOC_FREE(ctx);
500         } else {
501                 json_add_stringn(&value, "value", (char *)lv.data, len);
502         }
503         /*
504          * As array is a JSON array the element name is NULL
505          */
506         json_add_object(array, NULL, &value);
507 }
508
509 /*
510  * @brief Build a JSON object containing the attributes in an ldb_message.
511  *
512  * Build a JSON object containing all the attributes in an ldb_message.
513  * The attributes are keyed by attribute name, the values of "secret attributes"
514  * are supressed.
515  *
516  * {
517  *      "password":{
518  *              "redacted":true,
519  *              "action":"delete"
520  *      },
521  *      "name":{
522  *              "values": [
523  *                      {
524  *                              "value":"xxxxxxxx",
525  *                              "base64":true
526  *                              "truncated":true
527  *                      },
528  *              ],
529  *              "action":"add",
530  *      }
531  * }
532  *
533  * values is an array of json objects generated by add_ldb_value.
534  * redacted indicates that the attribute is secret.
535  * action is only set for modification operations.
536  *
537  * @param operation the ldb operation being performed
538  * @param message the ldb_message to process.
539  *
540  * @return A populated json object.
541  *
542  */
543 struct json_object dsdb_audit_attributes_json(
544         enum ldb_request_type operation,
545         const struct ldb_message* message)
546 {
547
548         struct json_object attributes = json_new_object();
549         int i, j;
550         for (i=0;i<message->num_elements;i++) {
551                 struct json_object actions;
552                 struct json_object attribute;
553                 struct json_object action = json_new_object();
554                 const char *name = message->elements[i].name;
555
556                 /*
557                  * If this is a modify operation tag the attribute with
558                  * the modification action.
559                  */
560                 if (operation == LDB_MODIFY) {
561                         const char *act = NULL;
562                         const int flags =  message->elements[i].flags;
563                         act = dsdb_audit_get_modification_action(flags);
564                         json_add_string(&action, "action" , act);
565                 }
566                 if (operation == LDB_ADD) {
567                         json_add_string(&action, "action" , "add");
568                 }
569
570                 /*
571                  * If the attribute is a secret attribute, tag it as redacted
572                  * and don't include the values
573                  */
574                 if (dsdb_audit_redact_attribute(name)) {
575                         json_add_bool(&action, "redacted", true);
576                 } else {
577                         struct json_object values;
578                         /*
579                          * Add the values for the action
580                          */
581                         values = json_new_array();
582                         for (j=0;j<message->elements[i].num_values;j++) {
583                                 dsdb_audit_add_ldb_value(
584                                         &values,
585                                         message->elements[i].values[j]);
586                         }
587                         json_add_object(&action, "values", &values);
588                 }
589                 attribute = json_get_object(&attributes, name);
590                 actions = json_get_array(&attribute, "actions");
591                 json_add_object(&actions, NULL, &action);
592                 json_add_object(&attribute, "actions", &actions);
593                 json_add_object(&attributes, name, &attribute);
594         }
595         return attributes;
596 }