Skip to content

Commit

Permalink
Hash-collision denial-of-service vulnerabilities (#87)
Browse files Browse the repository at this point in the history
* Add case insensitive SipHash implementation
* Replace ref hash function with SipHash
* Add label to link_ref struct.
* Update find_link_ref to compare link labels as well as hashes
* Update add_link_ref to disallow duplicate entries.
* cast to char from uint8_t for strncasecmp
* update README markdown, remove TODO
* add py2 wheel generation
* fix: add logic for older glibc not having getrandom, impacting staging

Co-authored-by: Nicolaas <nweidema@usc.edu>
  • Loading branch information
sp3nx0r and NicolaasWeideman committed Oct 20, 2021
1 parent 4a16cf0 commit 1ac2c13
Show file tree
Hide file tree
Showing 7 changed files with 447 additions and 27 deletions.
5 changes: 4 additions & 1 deletion Dockerfile.wheel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update && \
apt-get install -y \
python \
python-dev \
python-pip \
build-essential \
gperf \
software-properties-common
Expand All @@ -15,4 +18,4 @@ RUN mkdir -p $SRC_DIR $WHEEL_OUTPUT_DIR
WORKDIR $SRC_DIR

ADD . $SRC_DIR
CMD pip3 wheel --wheel-dir=$WHEEL_OUTPUT_DIR .
CMD pip3 wheel --wheel-dir=$WHEEL_OUTPUT_DIR . ; pip2 wheel --wheel-dir=$WHEEL_OUTPUT_DIR .
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
PACKAGE := snudown
VERSION := 1.6.0
VERSION := $(shell grep -oP 'SNUDOWN_VERSION "\K\d.\d.\d' snudown.c)
DOCKERFILE := Dockerfile.wheel

default: build run
default: clean build run

build:
echo $(VERSION)
docker build \
-t $(PACKAGE):$(VERSION) \
-f $(DOCKERFILE) \
Expand All @@ -17,3 +18,7 @@ run:
-v `pwd`/dist:/tmp/dist \
-it \
$(PACKAGE):$(VERSION)

clean:
rm -rf build
rm -rf dist
26 changes: 15 additions & 11 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@ Setup for development on Mac OS X
1. From `~/src/snudown` run `$ python setup.py build`
2. If this is successful, there will now be a `snudown.so` file in the `/snudown/build/lib.< os info >-< python version number>` directory
3. From within the `/lib.< os info >-< python version number>` directory, start a python interpreter
<!-- Make sure you can import snudown -->
>>> import snudown
<!-- verify that the build you just made is being used -->
>>> print(snudown.__file__)
snudown.so
<!-- Test the functionality of the build -->
>>> snudown.markdown('[hi](http://www.reddit.com)')
'<p><a href="http://www.reddit.com">hi</a></p>\n'
<!-- Great! You can exit now. -->
>>> quit()
```
<!-- Make sure you can import snudown -->
>>> import snudown
<!-- verify that the build you just made is being used -->
>>> print(snudown.__file__)
snudown.so
<!-- Test the functionality of the build -->
>>> snudown.markdown('[hi](http://www.reddit.com)')
'<p><a href="http://www.reddit.com">hi</a></p>\n'
<!-- Great! You can exit now. -->
>>> quit()
```
4. Verify that the tests pass
$ PYTHONPATH="$(pwd)" python ../../test_snudown.py
```
$ PYTHONPATH="$(pwd)" python ../../test_snudown.py
```
5. Verify that all the previous steps work for both Python 2 AND Python 3


Expand Down
2 changes: 1 addition & 1 deletion snudown.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "html.h"
#include "autolink.h"

#define SNUDOWN_VERSION "1.6.0"
#define SNUDOWN_VERSION "1.7.0"

enum snudown_renderer_mode {
RENDERER_USERTEXT = 0,
Expand Down
62 changes: 50 additions & 12 deletions src/markdown.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,20 @@

#include "markdown.h"
#include "stack.h"
#include "siphash.h"

#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>

#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 25
#include <sys/random.h>
#else
# define getrandom backport_getrandom
# include <sys/syscall.h>
#endif

#if defined(_WIN32)
#define strncasecmp _strnicmp
#endif
Expand All @@ -51,6 +59,7 @@ struct link_ref {
unsigned int id;

struct buf *link;
struct buf *label;
struct buf *title;

struct link_ref *next;
Expand Down Expand Up @@ -123,10 +132,18 @@ struct sd_markdown {
int in_link_body;
};

int sip_hash_key_init = 0;
uint8_t sip_hash_key[SIP_HASH_KEY_LEN];

/***************************
* HELPER FUNCTIONS *
***************************/

int backport_getrandom(void *buf, size_t buflen, unsigned int flags)
{
return (int)syscall(SYS_getrandom, buf, buflen, flags);
}

static inline struct buf *
rndr_newbuf(struct sd_markdown *rndr, int type)
{
Expand Down Expand Up @@ -175,26 +192,35 @@ unscape_text(struct buf *ob, struct buf *src)
static unsigned int
hash_link_ref(const uint8_t *link_ref, size_t length)
{
size_t i;
unsigned int hash = 0;

for (i = 0; i < length; ++i)
hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash;

return hash;
return siphash_nocase(link_ref, length, sip_hash_key);
}

static struct link_ref *
add_link_ref(
struct link_ref **references,
const uint8_t *name, size_t name_size)
{
struct link_ref *ref = calloc(1, sizeof(struct link_ref));
unsigned int hash;
struct link_ref *ref;
hash = hash_link_ref(name, name_size);
ref = references[hash % REF_TABLE_SIZE];
while (ref != NULL) {
/* If a reference with the same label exists already, replace it with the new reference */
if (ref->id == hash && ref->label->size == name_size) {
if (strncasecmp((char *)ref->label->data, (char *) name, name_size) == 0) {
bufrelease(ref->label);
bufrelease(ref->link);
bufrelease(ref->title);
return ref;
}
}

ref = ref->next;
}
ref = calloc(1, sizeof(struct link_ref));
if (!ref)
return NULL;

ref->id = hash_link_ref(name, name_size);
ref->id = hash;
ref->next = references[ref->id % REF_TABLE_SIZE];

references[ref->id % REF_TABLE_SIZE] = ref;
Expand All @@ -210,8 +236,11 @@ find_link_ref(struct link_ref **references, uint8_t *name, size_t length)
ref = references[hash % REF_TABLE_SIZE];

while (ref != NULL) {
if (ref->id == hash)
return ref;
if (ref->id == hash && ref->label->size == length) {
if (strncasecmp((char *)ref->label->data, (char *) name, length) == 0) {
return ref;
}
}

ref = ref->next;
}
Expand All @@ -230,6 +259,7 @@ free_link_refs(struct link_ref **references)

while (r) {
next = r->next;
bufrelease(r->label);
bufrelease(r->link);
bufrelease(r->title);
free(r);
Expand Down Expand Up @@ -2565,6 +2595,8 @@ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_re
if (!ref)
return 0;

ref->label = bufnew(id_end - id_offset);
bufput(ref->label, data + id_offset, id_end - id_offset);
ref->link = bufnew(link_end - link_offset);
bufput(ref->link, data + link_offset, link_end - link_offset);

Expand Down Expand Up @@ -2622,6 +2654,12 @@ sd_markdown_new(
if (!md)
return NULL;

if (!sip_hash_key_init) {
if (getrandom(sip_hash_key, SIP_HASH_KEY_LEN, 0) < SIP_HASH_KEY_LEN)
return NULL;
sip_hash_key_init = 1;
}

memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks));

stack_init(&md->work_bufs[BUFFER_BLOCK], 4);
Expand Down
Loading

0 comments on commit 1ac2c13

Please sign in to comment.