waf.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  1. // Unless explicitly stated otherwise all files in this repository are licensed
  2. // under the Apache License Version 2.0.
  3. // This product includes software developed at Datadog (https://www.datadoghq.com/).
  4. // Copyright 2016 Datadog, Inc.
  5. //go:build appsec && cgo && !windows && (amd64 || arm64) && (linux || darwin)
  6. // +build appsec
  7. // +build cgo
  8. // +build !windows
  9. // +build amd64 arm64
  10. // +build linux darwin
  11. package waf
  12. // #include <stdlib.h>
  13. // #include <string.h>
  14. // #include "ddwaf.h"
  15. // // Forward declaration of the Go function go_ddwaf_object_free which is a Go
  16. // // function defined and exported into C by CGO in this file.
  17. // // This allows to reference this symbol with the C wrapper and pass its
  18. // // pointer to ddwaf_context_init.
  19. // void go_ddwaf_object_free(ddwaf_object*);
  20. // #cgo CFLAGS: -I${SRCDIR}/include
  21. // #cgo linux,amd64 LDFLAGS: -L${SRCDIR}/lib/linux-amd64 -lddwaf -lm -ldl -Wl,-rpath=/lib64:/usr/lib64:/usr/local/lib64:/lib:/usr/lib:/usr/local/lib
  22. // #cgo linux,arm64 LDFLAGS: -L${SRCDIR}/lib/linux-arm64 -lddwaf -lm -ldl -Wl,-rpath=/lib64:/usr/lib64:/usr/local/lib64:/lib:/usr/lib:/usr/local/lib
  23. // #cgo darwin,amd64 LDFLAGS: -L${SRCDIR}/lib/darwin-amd64 -lddwaf -lc++
  24. // #cgo darwin,arm64 LDFLAGS: -L${SRCDIR}/lib/darwin-arm64 -lddwaf -lc++
  25. import "C"
  26. import (
  27. "encoding/json"
  28. "errors"
  29. "fmt"
  30. "math"
  31. "reflect"
  32. "strconv"
  33. "strings"
  34. "sync"
  35. "sync/atomic"
  36. "time"
  37. "unicode"
  38. "unsafe"
  39. rc "github.com/DataDog/datadog-agent/pkg/remoteconfig/state"
  40. // Do not remove the following imports which allow supporting package
  41. // vendoring by properly copying all the files needed by CGO: the libddwaf
  42. // header file and the static libraries.
  43. _ "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/include"
  44. _ "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/darwin-amd64"
  45. _ "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/darwin-arm64"
  46. _ "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/linux-amd64"
  47. _ "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/waf/lib/linux-arm64"
  48. )
  49. var wafVersion = getWAFVersion()
  50. // Health allows knowing if the WAF can be used. It returns a nil error when the WAF library is healthy.
  51. // Otherwise, it returns an error describing the issue.
  52. func Health() error {
  53. return nil
  54. }
  55. // Version returns the current version of the WAF
  56. func Version() string {
  57. return wafVersion
  58. }
  59. // Handle represents an instance of the WAF for a given ruleset.
  60. type Handle struct {
  61. // Instance of the WAF
  62. handle C.ddwaf_handle
  63. // Lock-less reference counter avoiding blocking calls to the Close() method
  64. // while WAF contexts are still using the WAF handle. Instead, we let the
  65. // release actually happen only when the reference counter reaches 0.
  66. // This can happen either from a request handler calling its WAF context's
  67. // Close() method, or either from the appsec instance calling the WAF
  68. // handle's Close() method when creating a new WAF handle with new rules.
  69. // Note that this means several instances of the WAF can exist at the same
  70. // time with their own set of rules. This choice was done to be able to
  71. // efficiently update the security rules concurrently, without having to
  72. // block the request handlers for the time of the security rules update.
  73. refCounter atomicRefCounter
  74. // RWMutex protecting the R/W accesses to the internal rules data (stored
  75. // in the handle).
  76. mu sync.RWMutex
  77. // encoder of Go values into ddwaf objects.
  78. encoder encoder
  79. // addresses the WAF rule is expecting.
  80. addresses []string
  81. // rulesetInfo holds information about rules initialization
  82. rulesetInfo RulesetInfo
  83. }
  84. // NewHandle creates a new instance of the WAF with the given JSON rule and key/value regexps for obfuscation.
  85. func NewHandle(jsonRule []byte, keyRegex, valueRegex string) (*Handle, error) {
  86. var rule interface{}
  87. if err := json.Unmarshal(jsonRule, &rule); err != nil {
  88. return nil, fmt.Errorf("could not parse the WAF rule: %v", err)
  89. }
  90. // Create a temporary unlimited encoder for the rules
  91. wafRule, err := newMaxEncoder().encode(rule)
  92. if err != nil {
  93. return nil, fmt.Errorf("could not encode the JSON WAF rule into a WAF object: %v", err)
  94. }
  95. defer freeWO(wafRule)
  96. // Run-time encoder limiting the size of the encoded values
  97. encoder := encoder{
  98. maxDepth: C.DDWAF_MAX_CONTAINER_DEPTH,
  99. maxStringLength: C.DDWAF_MAX_STRING_LENGTH,
  100. maxArrayLength: C.DDWAF_MAX_CONTAINER_SIZE,
  101. maxMapLength: C.DDWAF_MAX_CONTAINER_SIZE,
  102. }
  103. var wafRInfo C.ddwaf_ruleset_info
  104. keyRegexC, _, err := cstring(keyRegex, encoder.maxStringLength)
  105. if err != nil {
  106. return nil, fmt.Errorf("could not convert the obfuscator key regexp string to a C string: %v", err)
  107. }
  108. defer cFree(unsafe.Pointer(keyRegexC))
  109. valueRegexC, _, err := cstring(valueRegex, encoder.maxStringLength)
  110. if err != nil {
  111. return nil, fmt.Errorf("could not convert the obfuscator value regexp to a C string: %v", err)
  112. }
  113. defer cFree(unsafe.Pointer(valueRegexC))
  114. wafCfg := C.ddwaf_config{
  115. limits: struct{ max_container_size, max_container_depth, max_string_length C.uint32_t }{
  116. max_container_size: C.uint32_t(encoder.maxArrayLength),
  117. max_container_depth: C.uint32_t(encoder.maxMapLength),
  118. max_string_length: C.uint32_t(encoder.maxStringLength),
  119. },
  120. obfuscator: struct{ key_regex, value_regex *C.char }{
  121. key_regex: keyRegexC,
  122. value_regex: valueRegexC,
  123. },
  124. free_fn: C.ddwaf_object_free_fn(C.go_ddwaf_object_free),
  125. }
  126. defer C.ddwaf_ruleset_info_free(&wafRInfo)
  127. handle := C.ddwaf_init(wafRule.ctype(), &wafCfg, &wafRInfo)
  128. if handle == nil {
  129. return nil, errors.New("could not instantiate the waf rule")
  130. }
  131. incNbLiveCObjects()
  132. // Decode the ruleset information returned by the WAF
  133. errors, err := decodeErrors((*wafObject)(&wafRInfo.errors))
  134. if err != nil {
  135. C.ddwaf_destroy(handle)
  136. decNbLiveCObjects()
  137. return nil, err
  138. }
  139. rInfo := RulesetInfo{
  140. Failed: uint16(wafRInfo.failed),
  141. Loaded: uint16(wafRInfo.loaded),
  142. Version: C.GoString(wafRInfo.version),
  143. Errors: errors,
  144. }
  145. // Get the addresses the rule listens to
  146. addresses, err := ruleAddresses(handle)
  147. if err != nil {
  148. C.ddwaf_destroy(handle)
  149. decNbLiveCObjects()
  150. return nil, err
  151. }
  152. return &Handle{
  153. handle: handle,
  154. refCounter: 1,
  155. encoder: encoder,
  156. addresses: addresses,
  157. rulesetInfo: rInfo,
  158. }, nil
  159. }
  160. // Increment the ref counter and return true if the handle can be used, false
  161. // otherwise.
  162. func (h *Handle) incrementReferences() bool {
  163. return h.refCounter.increment() != 0
  164. }
  165. // Decrement the ref counter and release the memory when 0 is reached.
  166. func (h *Handle) decrementReferences() {
  167. if h.refCounter.decrement() == 0 {
  168. h.release()
  169. }
  170. }
  171. // Actual memory release of the WAF handle.
  172. func (h *Handle) release() {
  173. if h.handle == nil {
  174. return // already released - only happens if Close() is called more than once
  175. }
  176. C.ddwaf_destroy(h.handle)
  177. decNbLiveCObjects()
  178. h.handle = nil
  179. }
  180. func ruleAddresses(handle C.ddwaf_handle) ([]string, error) {
  181. var nbAddresses C.uint32_t
  182. caddresses := C.ddwaf_required_addresses(handle, &nbAddresses)
  183. if nbAddresses == 0 {
  184. return nil, ErrEmptyRuleAddresses
  185. }
  186. addresses := make([]string, int(nbAddresses))
  187. for i := 0; i < len(addresses); i++ {
  188. addresses[i] = C.GoString(cindexCharPtrArray(caddresses, i))
  189. }
  190. return addresses, nil
  191. }
  192. // Addresses returns the list of addresses the WAF rule is expecting.
  193. func (h *Handle) Addresses() []string {
  194. return h.addresses
  195. }
  196. // RulesetInfo returns the rules initialization metrics for the current WAF handle
  197. func (h *Handle) RulesetInfo() RulesetInfo {
  198. return h.rulesetInfo
  199. }
  200. // UpdateRulesData updates the data that some rules reference to.
  201. func (h *Handle) UpdateRulesData(data []rc.ASMDataRuleData) error {
  202. encoded, err := newMaxEncoder().encode(data) // FIXME: double-check with Anil that we are good with the current conversion of integers into strings here
  203. if err != nil {
  204. return fmt.Errorf("could not encode the JSON WAF rule data into a WAF object: %v", err)
  205. }
  206. defer freeWO(encoded)
  207. return h.updateRulesData(encoded)
  208. }
  209. // updateRulesData is the critical section of UpdateRulesData
  210. func (h *Handle) updateRulesData(data *wafObject) error {
  211. // Note about this lock: ddwaf_update_rule_data already is thread-safe to
  212. // use, but we chose to lock at the goroutine-level instead in order to
  213. // avoid locking OS threads and therefore prevent many other goroutines from
  214. // executing during that OS lock. If a goroutine locks due to this handle's
  215. // RWMutex, another goroutine gets executed on its OS thread.
  216. h.mu.Lock()
  217. defer h.mu.Unlock()
  218. rc := C.ddwaf_update_rule_data(h.handle, data.ctype())
  219. if rc != C.DDWAF_OK {
  220. return fmt.Errorf("unexpected error number `%d` while updating the WAF rule data", rc)
  221. }
  222. return nil
  223. }
  224. // Close the WAF handle. Note that this call doesn't block until the handle gets
  225. // released but instead let WAF contexts still use it until there's no more (eg.
  226. // when swapping the WAF handle with a new one).
  227. func (h *Handle) Close() {
  228. h.decrementReferences()
  229. }
  230. // Context is a WAF execution context. It allows to run the WAF incrementally
  231. // by calling it multiple times to run its rules every time new addresses
  232. // become available. Each request must have its own Context.
  233. type Context struct {
  234. // Instance of the WAF
  235. handle *Handle
  236. // Cumulated internal WAF run time - in nanoseconds - for this context.
  237. totalRuntimeNs AtomicU64
  238. // Cumulated overall run time - in nanoseconds - for this context.
  239. totalOverallRuntimeNs AtomicU64
  240. // Cumulated timeout count for this context.
  241. timeoutCount AtomicU64
  242. context C.ddwaf_context
  243. // Mutex protecting the use of context which is not thread-safe.
  244. mu sync.Mutex
  245. }
  246. // NewContext creates a new WAF context and increases the number of references
  247. // to the WAF handle.
  248. // A nil value is returned when the WAF handle can no longer be used or the
  249. // WAF context couldn't be created.
  250. func NewContext(handle *Handle) *Context {
  251. if !handle.incrementReferences() {
  252. return nil // The WAF handle got released
  253. }
  254. context := C.ddwaf_context_init(handle.handle)
  255. if context == nil {
  256. handle.decrementReferences()
  257. return nil
  258. }
  259. incNbLiveCObjects()
  260. return &Context{
  261. handle: handle,
  262. context: context,
  263. }
  264. }
  265. // Run the WAF with the given Go values and timeout.
  266. func (c *Context) Run(values map[string]interface{}, timeout time.Duration) (matches []byte, actions []string, err error) {
  267. now := time.Now()
  268. defer func() {
  269. dt := time.Since(now)
  270. c.totalOverallRuntimeNs.Add(uint64(dt.Nanoseconds()))
  271. }()
  272. if len(values) == 0 {
  273. return
  274. }
  275. wafValue, err := c.handle.encoder.encode(values)
  276. if err != nil {
  277. return nil, nil, err
  278. }
  279. return c.run(wafValue, timeout)
  280. }
  281. // run is the critical section of Run
  282. func (c *Context) run(data *wafObject, timeout time.Duration) (matches []byte, actions []string, err error) {
  283. // Exclusively lock this WAF context for the time of this run
  284. c.mu.Lock()
  285. defer c.mu.Unlock()
  286. // RLock the handle to safely get read access to the WAF handle and prevent concurrent changes of it
  287. // such as a rules-data update.
  288. c.handle.mu.RLock()
  289. defer c.handle.mu.RUnlock()
  290. var result C.ddwaf_result
  291. defer freeWAFResult(&result)
  292. rc := C.ddwaf_run(c.context, data.ctype(), &result, C.uint64_t(timeout/time.Microsecond))
  293. c.totalRuntimeNs.Add(uint64(result.total_runtime))
  294. matches, actions, err = goReturnValues(rc, &result)
  295. if err == ErrTimeout {
  296. c.timeoutCount.Inc()
  297. }
  298. return matches, actions, err
  299. }
  300. // Close the WAF context by releasing its C memory and decreasing the number of
  301. // references to the WAF handle.
  302. func (c *Context) Close() {
  303. // RUnlock the WAF RWMutex to decrease the count of WAF Contexts using it.
  304. defer c.handle.decrementReferences()
  305. C.ddwaf_context_destroy(c.context)
  306. decNbLiveCObjects()
  307. }
  308. // TotalRuntime returns the cumulated WAF runtime across various run calls within the same WAF context.
  309. // Returned time is in nanoseconds.
  310. func (c *Context) TotalRuntime() (overallRuntimeNs, internalRuntimeNs uint64) {
  311. return c.totalOverallRuntimeNs.Load(), c.totalRuntimeNs.Load()
  312. }
  313. // TotalTimeouts returns the cumulated amount of WAF timeouts across various run calls within the same WAF context.
  314. func (c *Context) TotalTimeouts() uint64 {
  315. return c.timeoutCount.Load()
  316. }
  317. // Translate libddwaf return values into return values suitable to a Go program.
  318. // Note that it is possible to have matches or actions even if err is not nil in
  319. // case of a timeout during the WAF call.
  320. func goReturnValues(rc C.DDWAF_RET_CODE, result *C.ddwaf_result) (matches []byte, actions []string, err error) {
  321. if bool(result.timeout) {
  322. err = ErrTimeout
  323. }
  324. switch rc {
  325. case C.DDWAF_OK:
  326. return nil, nil, err
  327. case C.DDWAF_MATCH:
  328. if result.data != nil {
  329. matches = C.GoBytes(unsafe.Pointer(result.data), C.int(C.strlen(result.data)))
  330. }
  331. if size := result.actions.size; size > 0 {
  332. cactions := result.actions.array
  333. actions = make([]string, size)
  334. for i := 0; i < int(size); i++ {
  335. actions[i] = C.GoString(cindexCharPtrArray(cactions, i))
  336. }
  337. }
  338. return matches, actions, err
  339. default:
  340. return nil, nil, goRunError(rc)
  341. }
  342. }
  343. func goRunError(rc C.DDWAF_RET_CODE) error {
  344. switch rc {
  345. case C.DDWAF_ERR_INTERNAL:
  346. return ErrInternal
  347. case C.DDWAF_ERR_INVALID_OBJECT:
  348. return ErrInvalidObject
  349. case C.DDWAF_ERR_INVALID_ARGUMENT:
  350. return ErrInvalidArgument
  351. default:
  352. return fmt.Errorf("unknown waf return code %d", int(rc))
  353. }
  354. }
  355. func getWAFVersion() string {
  356. cversion := C.ddwaf_get_version() // static mem pointer returned - no need to free it
  357. return C.GoString(cversion)
  358. }
  359. // Errors the encoder and decoder can return.
  360. var (
  361. errMaxDepth = errors.New("max depth reached")
  362. errUnsupportedValue = errors.New("unsupported Go value")
  363. errOutOfMemory = errors.New("out of memory")
  364. errInvalidMapKey = errors.New("invalid WAF object map key")
  365. errNilObjectPtr = errors.New("nil WAF object pointer")
  366. )
  367. // isIgnoredValueError returns true if the error is only about ignored Go values
  368. // (errUnsupportedValue or errMaxDepth).
  369. func isIgnoredValueError(err error) bool {
  370. return err == errUnsupportedValue || err == errMaxDepth
  371. }
  372. // encoder is allows to encode a Go value to a WAF object
  373. type encoder struct {
  374. // Maximum depth a WAF object can have. Every Go value further this depth is
  375. // ignored and not encoded into a WAF object.
  376. maxDepth int
  377. // Maximum string length. A string longer than this length is truncated to
  378. // this length.
  379. maxStringLength int
  380. // Maximum string length. Everything further this length is ignored.
  381. maxArrayLength int
  382. // Maximum map length. Everything further this length is ignored. Given the
  383. // fact Go maps are unordered, it means WAF map objects created from Go maps
  384. // larger than this length will have random keys.
  385. maxMapLength int
  386. }
  387. func newMaxEncoder() *encoder {
  388. const intSize = 32 << (^uint(0) >> 63) // copied from recent versions of math.MaxInt
  389. const maxInt = 1<<(intSize-1) - 1 // copied from recent versions of math.MaxInt
  390. return &encoder{
  391. maxDepth: maxInt,
  392. maxStringLength: maxInt,
  393. maxArrayLength: maxInt,
  394. maxMapLength: maxInt,
  395. }
  396. }
  397. func (e *encoder) encode(v interface{}) (object *wafObject, err error) {
  398. defer func() {
  399. if v := recover(); v != nil {
  400. err = fmt.Errorf("waf panic: %v", v)
  401. }
  402. if err != nil && object != nil {
  403. freeWO(object)
  404. }
  405. }()
  406. wo := &wafObject{}
  407. err = e.encodeValue(reflect.ValueOf(v), wo, e.maxDepth)
  408. if err != nil {
  409. return nil, err
  410. }
  411. return wo, nil
  412. }
  413. func (e *encoder) encodeValue(v reflect.Value, wo *wafObject, depth int) error {
  414. switch kind := v.Kind(); kind {
  415. default:
  416. return errUnsupportedValue
  417. case reflect.Bool:
  418. var b string
  419. if v.Bool() {
  420. b = "true"
  421. } else {
  422. b = "false"
  423. }
  424. return e.encodeString(b, wo)
  425. case reflect.Ptr, reflect.Interface:
  426. // The traversal of pointer and interfaces is not accounted in the depth
  427. // as it has no impact on the WAF object depth
  428. return e.encodeValue(v.Elem(), wo, depth)
  429. case reflect.String:
  430. return e.encodeString(v.String(), wo)
  431. case reflect.Struct:
  432. if depth < 0 {
  433. return errMaxDepth
  434. }
  435. return e.encodeStruct(v, wo, depth-1)
  436. case reflect.Map:
  437. if depth < 0 {
  438. return errMaxDepth
  439. }
  440. return e.encodeMap(v, wo, depth-1)
  441. case reflect.Array, reflect.Slice:
  442. if depth < 0 {
  443. return errMaxDepth
  444. }
  445. if v.Type() == reflect.TypeOf([]byte(nil)) {
  446. return e.encodeString(string(v.Bytes()), wo)
  447. }
  448. return e.encodeArray(v, wo, depth-1)
  449. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  450. return e.encodeInt64(v.Int(), wo)
  451. case reflect.Float32, reflect.Float64:
  452. return e.encodeInt64(int64(math.Round(v.Float())), wo)
  453. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  454. return e.encodeUint64(v.Uint(), wo)
  455. }
  456. }
  457. func (e *encoder) encodeStruct(v reflect.Value, wo *wafObject, depth int) error {
  458. // Consider the number of struct fields as the WAF map capacity as some
  459. // struct fields might not be supported and ignored.
  460. typ := v.Type()
  461. nbFields := typ.NumField()
  462. capacity := nbFields
  463. if capacity > e.maxMapLength {
  464. capacity = e.maxMapLength
  465. }
  466. if err := wo.setMapContainer(C.size_t(capacity)); err != nil {
  467. return err
  468. }
  469. // Encode struct fields
  470. length := 0
  471. for i := 0; length < capacity && i < nbFields; i++ {
  472. field := typ.Field(i)
  473. // Skip private fields
  474. fieldName := field.Name
  475. if len(fieldName) < 1 || unicode.IsLower(rune(fieldName[0])) {
  476. continue
  477. }
  478. // Use the json tag name as field name if present
  479. if tag, ok := field.Tag.Lookup("json"); ok {
  480. if i := strings.IndexByte(tag, byte(',')); i > 0 {
  481. tag = tag[:i]
  482. }
  483. if len(tag) > 0 {
  484. fieldName = tag
  485. }
  486. }
  487. mapEntry := wo.index(C.uint64_t(length))
  488. if err := e.encodeMapKey(reflect.ValueOf(fieldName), mapEntry); isIgnoredValueError(err) {
  489. continue
  490. }
  491. if err := e.encodeValue(v.Field(i), mapEntry, depth); err != nil {
  492. // Free the map entry in order to free the previously allocated map key
  493. freeWO(mapEntry)
  494. if isIgnoredValueError(err) {
  495. continue
  496. }
  497. return err
  498. }
  499. length++
  500. }
  501. // Update the map length to the actual one
  502. if length != capacity {
  503. wo.setLength(C.uint64_t(length))
  504. }
  505. return nil
  506. }
  507. func (e *encoder) encodeMap(v reflect.Value, wo *wafObject, depth int) error {
  508. // Consider the Go map value length the WAF map capacity as some map entries
  509. // might not be supported and ignored.
  510. // In this case, the actual map length will be lesser than the Go map value
  511. // length.
  512. capacity := v.Len()
  513. if capacity > e.maxMapLength {
  514. capacity = e.maxMapLength
  515. }
  516. if err := wo.setMapContainer(C.size_t(capacity)); err != nil {
  517. return err
  518. }
  519. // Encode map entries
  520. length := 0
  521. for iter := v.MapRange(); iter.Next(); {
  522. if length == capacity {
  523. break
  524. }
  525. mapEntry := wo.index(C.uint64_t(length))
  526. if err := e.encodeMapKey(iter.Key(), mapEntry); isIgnoredValueError(err) {
  527. continue
  528. }
  529. if err := e.encodeValue(iter.Value(), mapEntry, depth); err != nil {
  530. // Free the previously allocated map key
  531. freeWO(mapEntry)
  532. if isIgnoredValueError(err) {
  533. continue
  534. }
  535. return err
  536. }
  537. length++
  538. }
  539. // Update the map length to the actual one
  540. if length != capacity {
  541. wo.setLength(C.uint64_t(length))
  542. }
  543. return nil
  544. }
  545. func (e *encoder) encodeMapKey(v reflect.Value, wo *wafObject) error {
  546. for {
  547. switch v.Kind() {
  548. default:
  549. return errUnsupportedValue
  550. case reflect.Ptr, reflect.Interface:
  551. if v.IsNil() {
  552. return errUnsupportedValue
  553. }
  554. v = v.Elem()
  555. case reflect.String:
  556. ckey, length, err := cstring(v.String(), e.maxStringLength)
  557. if err != nil {
  558. return err
  559. }
  560. wo.setMapKey(ckey, C.uint64_t(length))
  561. return nil
  562. }
  563. }
  564. }
  565. func (e *encoder) encodeArray(v reflect.Value, wo *wafObject, depth int) error {
  566. // Consider the array length as a capacity as some array values might not be supported and ignored. In this case,
  567. // the actual length will be lesser than the Go value length.
  568. length := v.Len()
  569. capacity := length
  570. if capacity > e.maxArrayLength {
  571. capacity = e.maxArrayLength
  572. }
  573. if err := wo.setArrayContainer(C.size_t(capacity)); err != nil {
  574. return err
  575. }
  576. // Walk the array until we successfully added up to "cap" elements or the Go array length was reached
  577. currIndex := 0
  578. for i := 0; currIndex < capacity && i < length; i++ {
  579. if err := e.encodeValue(v.Index(i), wo.index(C.uint64_t(currIndex)), depth); err != nil {
  580. if isIgnoredValueError(err) {
  581. continue
  582. }
  583. return err
  584. }
  585. // The value has been successfully encoded and added to the array
  586. currIndex++
  587. }
  588. // Update the array length to its actual value in case some array values where ignored
  589. if currIndex != capacity {
  590. wo.setLength(C.uint64_t(currIndex))
  591. }
  592. return nil
  593. }
  594. func (e *encoder) encodeString(str string, wo *wafObject) error {
  595. cstr, length, err := cstring(str, e.maxStringLength)
  596. if err != nil {
  597. return err
  598. }
  599. wo.setString(cstr, C.uint64_t(length))
  600. return nil
  601. }
  602. func (e *encoder) encodeInt64(n int64, wo *wafObject) error {
  603. // As of libddwaf v1.0.16, it currently expects numbers as strings
  604. // TODO(Julio-Guerra): clarify with libddwaf when should it be an actual
  605. // int64
  606. return e.encodeString(strconv.FormatInt(n, 10), wo)
  607. }
  608. func (e *encoder) encodeUint64(n uint64, wo *wafObject) error {
  609. // As of libddwaf v1.0.16, it currently expects numbers as strings
  610. // TODO(Julio-Guerra): clarify with libddwaf when should it be an actual
  611. // uint64
  612. return e.encodeString(strconv.FormatUint(n, 10), wo)
  613. }
  614. func decodeErrors(wo *wafObject) (map[string]interface{}, error) {
  615. v, err := decodeMap(wo)
  616. if err != nil {
  617. return nil, err
  618. }
  619. if len(v) == 0 {
  620. v = nil // enforce a nil map when the ddwaf map was empty
  621. }
  622. return v, nil
  623. }
  624. func decodeObject(wo *wafObject) (v interface{}, err error) {
  625. if wo == nil {
  626. return nil, errNilObjectPtr
  627. }
  628. switch wo._type {
  629. case wafUintType:
  630. return uint64(*wo.uint64ValuePtr()), nil
  631. case wafIntType:
  632. return int64(*wo.int64ValuePtr()), nil
  633. case wafStringType:
  634. return gostring(*wo.stringValuePtr(), wo.length())
  635. case wafArrayType:
  636. return decodeArray(wo)
  637. case wafMapType: // could be a map or a struct, no way to differentiate
  638. return decodeMap(wo)
  639. default:
  640. return nil, errUnsupportedValue
  641. }
  642. }
  643. func decodeArray(wo *wafObject) ([]interface{}, error) {
  644. if wo == nil {
  645. return nil, errNilObjectPtr
  646. }
  647. var err error
  648. len := wo.length()
  649. arr := make([]interface{}, len)
  650. for i := C.uint64_t(0); i < len && err == nil; i++ {
  651. arr[i], err = decodeObject(wo.index(i))
  652. }
  653. return arr, err
  654. }
  655. func decodeMap(wo *wafObject) (map[string]interface{}, error) {
  656. if wo == nil {
  657. return nil, errNilObjectPtr
  658. }
  659. length := wo.length()
  660. decodedMap := make(map[string]interface{}, length)
  661. for i := C.uint64_t(0); i < length; i++ {
  662. obj := wo.index(i)
  663. key, err := decodeMapKey(obj)
  664. if err != nil {
  665. return nil, err
  666. }
  667. val, err := decodeObject(obj)
  668. if err != nil {
  669. return nil, err
  670. }
  671. decodedMap[key] = val
  672. }
  673. return decodedMap, nil
  674. }
  675. func decodeMapKey(wo *wafObject) (string, error) {
  676. if wo == nil {
  677. return "", errNilObjectPtr
  678. }
  679. if wo.parameterNameLength == 0 || wo.mapKey() == nil {
  680. return "", errInvalidMapKey
  681. }
  682. return gostring(wo.mapKey(), wo.parameterNameLength)
  683. }
  684. const (
  685. wafUintType = C.DDWAF_OBJ_UNSIGNED
  686. wafIntType = C.DDWAF_OBJ_SIGNED
  687. wafStringType = C.DDWAF_OBJ_STRING
  688. wafArrayType = C.DDWAF_OBJ_ARRAY
  689. wafMapType = C.DDWAF_OBJ_MAP
  690. wafInvalidType = C.DDWAF_OBJ_INVALID
  691. )
  692. // wafObject is a Go wrapper allowing to create, access and destroy a WAF object
  693. // C structure.
  694. type wafObject C.ddwaf_object
  695. func (v *wafObject) ctype() *C.ddwaf_object { return (*C.ddwaf_object)(v) }
  696. // Return the pointer to the union field. It can be cast to the union type that needs to be accessed.
  697. func (v *wafObject) valuePtr() unsafe.Pointer { return unsafe.Pointer(&v.anon0[0]) }
  698. func (v *wafObject) arrayValuePtr() **C.ddwaf_object { return (**C.ddwaf_object)(v.valuePtr()) }
  699. func (v *wafObject) int64ValuePtr() *C.int64_t { return (*C.int64_t)(v.valuePtr()) }
  700. func (v *wafObject) uint64ValuePtr() *C.uint64_t { return (*C.uint64_t)(v.valuePtr()) }
  701. func (v *wafObject) stringValuePtr() **C.char { return (**C.char)(v.valuePtr()) }
  702. func (v *wafObject) setUint64(n C.uint64_t) {
  703. v._type = wafUintType
  704. *v.uint64ValuePtr() = n
  705. }
  706. func (v *wafObject) setInt64(n C.int64_t) {
  707. v._type = wafIntType
  708. *v.int64ValuePtr() = n
  709. }
  710. func (v *wafObject) setString(str *C.char, length C.uint64_t) {
  711. v._type = wafStringType
  712. v.nbEntries = C.uint64_t(length)
  713. *v.stringValuePtr() = str
  714. }
  715. func (v *wafObject) string() *C.char {
  716. return *v.stringValuePtr()
  717. }
  718. func (v *wafObject) setInvalid() {
  719. *v = wafObject{}
  720. }
  721. func (v *wafObject) setContainer(typ C.DDWAF_OBJ_TYPE, length C.size_t) error {
  722. // Allocate the zero'd array.
  723. var a *C.ddwaf_object
  724. if length > 0 {
  725. a = (*C.ddwaf_object)(C.calloc(length, C.sizeof_ddwaf_object))
  726. if a == nil {
  727. return ErrOutOfMemory
  728. }
  729. incNbLiveCObjects()
  730. *v.arrayValuePtr() = a
  731. v.setLength(C.uint64_t(length))
  732. }
  733. v._type = typ
  734. return nil
  735. }
  736. func (v *wafObject) setArrayContainer(length C.size_t) error {
  737. return v.setContainer(wafArrayType, length)
  738. }
  739. func (v *wafObject) setMapContainer(length C.size_t) error {
  740. return v.setContainer(wafMapType, length)
  741. }
  742. func (v *wafObject) setMapKey(key *C.char, length C.uint64_t) {
  743. v.parameterName = key
  744. v.parameterNameLength = length
  745. }
  746. func (v *wafObject) mapKey() *C.char {
  747. return v.parameterName
  748. }
  749. func (v *wafObject) setLength(length C.uint64_t) {
  750. v.nbEntries = length
  751. }
  752. func (v *wafObject) length() C.uint64_t {
  753. return v.nbEntries
  754. }
  755. func (v *wafObject) index(i C.uint64_t) *wafObject {
  756. if C.uint64_t(i) >= v.nbEntries {
  757. panic(errors.New("out of bounds access to waf array"))
  758. }
  759. // Go pointer arithmetic equivalent to the C expression `a->value.array[i]`
  760. base := uintptr(unsafe.Pointer(*v.arrayValuePtr()))
  761. return (*wafObject)(unsafe.Pointer(base + C.sizeof_ddwaf_object*uintptr(i)))
  762. }
  763. // Helper functions for testing, where direct cgo import is not allowed
  764. func toCInt64(v int) C.int64_t {
  765. return C.int64_t(v)
  766. }
  767. func toCUint64(v uint) C.uint64_t {
  768. return C.uint64_t(v)
  769. }
  770. // nbLiveCObjects is a simple monitoring of the number of C allocations.
  771. // Tests can read the value to check the count is back to 0.
  772. var nbLiveCObjects uint64
  773. func incNbLiveCObjects() {
  774. atomic.AddUint64(&nbLiveCObjects, 1)
  775. }
  776. func decNbLiveCObjects() {
  777. atomic.AddUint64(&nbLiveCObjects, ^uint64(0))
  778. }
  779. // gostring returns the Go version of the C string `str`, copying at most `len` bytes from the original string.
  780. func gostring(str *C.char, len C.uint64_t) (string, error) {
  781. if str == nil {
  782. return "", ErrInvalidArgument
  783. }
  784. goLen := C.int(len)
  785. if C.uint64_t(goLen) != len {
  786. return "", ErrInvalidArgument
  787. }
  788. return C.GoStringN(str, goLen), nil
  789. }
  790. // cstring returns the C string of the given Go string `str` with up to maxWAFStringSize bytes, along with the string
  791. // size that was allocated and copied.
  792. func cstring(str string, maxLength int) (*C.char, int, error) {
  793. // Limit the maximum string size to copy
  794. l := len(str)
  795. if l > maxLength {
  796. l = maxLength
  797. }
  798. // Copy the string up to l.
  799. // The copy is required as the pointer will be stored into the C structures,
  800. // so using a Go pointer is impossible.
  801. cstr := C.CString(str[:l])
  802. if cstr == nil {
  803. return nil, 0, errOutOfMemory
  804. }
  805. incNbLiveCObjects()
  806. return cstr, l, nil
  807. }
  808. func freeWO(v *wafObject) {
  809. if v == nil {
  810. return
  811. }
  812. // Free the map key if any
  813. if key := v.mapKey(); key != nil {
  814. cFree(unsafe.Pointer(v.parameterName))
  815. }
  816. // Free allocated values
  817. switch v._type {
  818. case wafInvalidType:
  819. return
  820. case wafStringType:
  821. cFree(unsafe.Pointer(v.string()))
  822. case wafMapType, wafArrayType:
  823. freeWOContainer(v)
  824. }
  825. // Make the value invalid to make it unusable
  826. v.setInvalid()
  827. }
  828. func freeWOContainer(v *wafObject) {
  829. length := v.length()
  830. for i := C.uint64_t(0); i < length; i++ {
  831. freeWO(v.index(i))
  832. }
  833. if a := *v.arrayValuePtr(); a != nil {
  834. cFree(unsafe.Pointer(a))
  835. }
  836. }
  837. func cFree(ptr unsafe.Pointer) {
  838. C.free(ptr)
  839. decNbLiveCObjects()
  840. }
  841. // Exported Go function to free ddwaf objects by using freeWO in order to keep
  842. // its dummy but efficient memory kallocation monitoring.
  843. //
  844. //export go_ddwaf_object_free
  845. func go_ddwaf_object_free(v *C.ddwaf_object) {
  846. freeWO((*wafObject)(v))
  847. }
  848. // Go reimplementation of ddwaf_result_free to avoid yet another CGO call in the
  849. // request hot-path and avoiding it when there are no results to free.
  850. func freeWAFResult(result *C.ddwaf_result) {
  851. if data := result.data; data != nil {
  852. C.free(unsafe.Pointer(data))
  853. }
  854. if array := result.actions.array; array != nil {
  855. for i := 0; i < int(result.actions.size); i++ {
  856. C.free(unsafe.Pointer(cindexCharPtrArray(array, i)))
  857. }
  858. C.free(unsafe.Pointer(array))
  859. }
  860. }
  861. // Helper function to access to i-th element of the given **C.char array.
  862. func cindexCharPtrArray(array **C.char, i int) *C.char {
  863. // Go pointer arithmetic equivalent to the C expression `array[i]`
  864. base := uintptr(unsafe.Pointer(array))
  865. return *(**C.char)(unsafe.Pointer(base + unsafe.Sizeof((*C.char)(nil))*uintptr(i)))
  866. }
  867. // Atomic reference counter helper initialized at 1 so that 0 is the special
  868. // value meaning that the object was released and is no longer usable.
  869. type atomicRefCounter uint32
  870. func (c *atomicRefCounter) unwrap() *uint32 {
  871. return (*uint32)(c)
  872. }
  873. // Add delta to reference counter.
  874. // It relies on a CAS spin-loop implementation in order to avoid changing the
  875. // counter when 0 has been reached.
  876. func (c *atomicRefCounter) add(delta uint32) uint32 {
  877. addr := c.unwrap()
  878. for {
  879. current := atomic.LoadUint32(addr)
  880. if current == 0 {
  881. // The object was released
  882. return 0
  883. }
  884. new := current + delta
  885. if swapped := atomic.CompareAndSwapUint32(addr, current, new); swapped {
  886. return new
  887. }
  888. }
  889. }
  890. // Increment the reference counter.
  891. // CAS spin-loop implementation in order to enforce +1 cannot happen when 0 has
  892. // been reached.
  893. func (c *atomicRefCounter) increment() uint32 {
  894. return c.add(1)
  895. }
  896. // Decrement the reference counter.
  897. // CAS spin-loop implementation in order to enforce +1 cannot happen when 0 has
  898. // been reached.
  899. func (c *atomicRefCounter) decrement() uint32 {
  900. const d = ^uint32(0)
  901. return c.add(d)
  902. }