1 /* $NetBSD: rbacaudit.c,v 1.2 2021/08/14 16:14:53 christos Exp $ */ 2 3 /* rbacaudit.c - RBAC Audit */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 /* ACKNOWLEDGEMENTS: 19 */ 20 21 #include <sys/cdefs.h> 22 __RCSID("$NetBSD: rbacaudit.c,v 1.2 2021/08/14 16:14:53 christos Exp $"); 23 24 #include "portable.h" 25 26 #include <stdio.h> 27 28 #include <ac/string.h> 29 30 #include "slap.h" 31 #include "slap-config.h" 32 #include "lutil.h" 33 34 #include "rbac.h" 35 36 static struct rbac_audit_op { 37 audit_op_t op; 38 struct berval op_bv; 39 } rbac_audit_ops[] = { 40 { CreateSession, BER_BVC("CreateSession") }, 41 { CheckAccess, BER_BVC("CheckAccess") }, 42 { AddActiveRole, BER_BVC("AddActiveRole") }, 43 { DropActiveRole, BER_BVC("DropActiveRole") }, 44 { SessionPermissions, BER_BVC("SessionPermissions") }, 45 { DeleteSession, BER_BVC("DeleteSession") }, 46 { SessionRoles, BER_BVC("SessionRoles") }, 47 48 { -1, BER_BVNULL } 49 }; 50 51 static int 52 rbac_audit_fake_cb( Operation *op, SlapReply *rs ) 53 { 54 Debug( LDAP_DEBUG_ANY, "rbac_audit_fake_cb\n" ); 55 56 return 0; 57 } 58 59 void 60 rbac_audit( 61 Operation *op, 62 audit_op_t rbac_op, 63 rbac_session_t *sessp, 64 rbac_req_t *reqp, 65 int result, 66 char *msg ) 67 { 68 int op_idx, rc = LDAP_SUCCESS; 69 int found = 0; 70 struct berval timestamp; 71 tenant_info_t *tenantp = rbac_tid2tenant( &sessp->tenantid ); 72 slap_callback cb = { 0 }; 73 SlapReply rs2 = { REP_RESULT }; 74 Entry *e = NULL; 75 struct berval auditObjectClass = BER_BVC("rbacAudit"); 76 struct berval auditResultSuccess = BER_BVC("success"); 77 struct berval auditResultFailed = BER_BVC("failed"); 78 struct berval bv, rdn, nrdn; 79 char rdnbuf[RBAC_BUFLEN]; 80 time_t now; 81 char nowstr[LDAP_LUTIL_GENTIME_BUFSIZE]; 82 83 for ( op_idx = 0; rbac_audit_ops[op_idx].op != -1; op_idx++ ) { 84 if ( rbac_op == rbac_audit_ops[op_idx].op ) { 85 /* legit audit op */ 86 found = 1; 87 break; 88 } 89 } 90 91 if ( !found ) goto done; 92 93 e = entry_alloc(); 94 95 /* audit timestamp */ 96 now = slap_get_time(); /* stored for later consideration */ 97 timestamp.bv_val = nowstr; 98 timestamp.bv_len = sizeof(nowstr); 99 slap_timestamp( &now, ×tamp ); 100 101 /* construct audit record DN; FIXME: random() call */ 102 sprintf( rdnbuf, "%s%d", RBAC_AUDIT_RDN_EQ, (int)op->o_tid ); 103 strcat( rdnbuf, "-" ); 104 strncat( rdnbuf, timestamp.bv_val, timestamp.bv_len ); 105 bv.bv_val = &rdnbuf[0]; 106 bv.bv_len = strlen( &rdnbuf[0] ); 107 108 rc = dnPrettyNormal( NULL, &bv, &rdn, &nrdn, NULL ); 109 if ( rc != LDAP_SUCCESS ) { 110 Debug( LDAP_DEBUG_ANY, "rbac_audit: " 111 "unable to normalize audit rDN (rc=%d)\n", rc ); 112 goto done; 113 } 114 115 /* FIXME: audit_basedn should have been normalized */ 116 build_new_dn( &e->e_name, &tenantp->audit_basedn, &rdn, NULL ); 117 build_new_dn( &e->e_nname, &tenantp->audit_basedn, &nrdn, NULL ); 118 119 ch_free( rdn.bv_val ); 120 ch_free( nrdn.bv_val ); 121 122 /* add timestamp */ 123 attr_merge_one( e, slap_rbac_schema.ad_audit_timestamp, ×tamp, NULL ); 124 125 /* add rbac audit objectClass */ 126 127 attr_merge_one( e, slap_schema.si_ad_objectClass, &auditObjectClass, NULL ); 128 attr_merge_one( e, slap_schema.si_ad_structuralObjectClass, 129 &auditObjectClass, NULL ); 130 131 /* audit op */ 132 attr_merge_one( e, slap_rbac_schema.ad_audit_op, 133 &rbac_audit_ops[op_idx].op_bv, NULL ); 134 135 /* userid */ 136 if ( sessp && !BER_BVISNULL( &sessp->uid ) ) { 137 attr_merge_one( e, slap_schema.si_ad_uid, &sessp->uid, NULL ); 138 } 139 140 /* session id */ 141 142 if ( sessp && !BER_BVISNULL( &sessp->sessid ) ) { 143 AttributeDescription *ad = NULL; 144 const char *text = NULL; 145 struct berval sessid = BER_BVC("rbacSessid"); 146 147 rc = slap_bv2ad( &sessid, &ad, &text ); 148 if ( rc != LDAP_SUCCESS ) { 149 goto done; 150 } 151 attr_merge_one( e, ad, &sessp->sessid, NULL ); 152 } 153 154 /* audit result */ 155 attr_merge_one( e, slap_rbac_schema.ad_audit_result, 156 result == LDAP_SUCCESS ? &auditResultSuccess : &auditResultFailed, 157 NULL ); 158 159 switch ( rbac_op ) { 160 case CreateSession: 161 /* audit roles */ 162 if ( sessp && sessp->roles ) { 163 attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles, 164 NULL ); 165 } 166 if ( reqp && reqp->roles ) { 167 attr_merge( e, slap_rbac_schema.ad_audit_requested_roles, 168 reqp->roles, NULL ); 169 } 170 break; 171 172 case CheckAccess: 173 if ( sessp && sessp->roles ) { 174 attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles, 175 NULL ); 176 } 177 if ( reqp && !BER_BVISEMPTY( &reqp->opname ) ) { 178 attr_merge_one( e, slap_rbac_schema.ad_audit_operations, 179 &reqp->opname, NULL ); 180 } 181 if ( reqp && !BER_BVISEMPTY( &reqp->objname ) ) { 182 attr_merge_one( e, slap_rbac_schema.ad_audit_objects, 183 &reqp->objname, NULL ); 184 } 185 break; 186 187 case AddActiveRole: 188 if ( reqp && reqp->roles ) { 189 attr_merge( e, slap_rbac_schema.ad_audit_requested_roles, 190 reqp->roles, NULL ); 191 } 192 break; 193 194 case DropActiveRole: 195 /* audit roles */ 196 if ( reqp && reqp->roles ) { 197 attr_merge( e, slap_rbac_schema.ad_audit_requested_roles, 198 reqp->roles, NULL ); 199 } 200 break; 201 202 case SessionPermissions: 203 if ( sessp && sessp->roles ) { 204 attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles, 205 NULL ); 206 } 207 break; 208 209 case DeleteSession: 210 case SessionRoles: 211 default: 212 break; 213 } 214 215 /* record the audit record */ 216 Operation op2 = *op; 217 rbac_callback_info_t rbac_cb; 218 cb.sc_private = &rbac_cb; 219 cb.sc_response = rbac_audit_fake_cb; 220 op2.o_callback = &cb; 221 222 op2.o_tag = LDAP_REQ_ADD; 223 op2.o_protocol = LDAP_VERSION3; 224 op2.o_req_dn = e->e_name; 225 op2.o_req_ndn = e->e_nname; 226 op2.ora_e = e; 227 op2.o_bd = select_backend( &op2.o_req_ndn, 0 ); 228 op2.o_dn = op2.o_bd->be_rootdn; 229 op2.o_ndn = op2.o_bd->be_rootndn; 230 231 op2.ors_limit = NULL; 232 rc = op2.o_bd->be_add( &op2, &rs2 ); 233 234 done: 235 if ( e ) entry_free( e ); 236 237 return; 238 } 239