From eb2ee9e570c2d49b4516561d8ea848b0464f7b29 Mon Sep 17 00:00:00 2001 From: oupson Date: Mon, 25 Oct 2021 09:39:30 +0200 Subject: [PATCH] Initial commit --- .clang-format | 1 + .gitignore | 6 + Makefile | 39 +++++++ README.md | 1 + include/auth_univ_orleans.h | 9 ++ include/cookie_iterator.h | 39 +++++++ src/auth_univ_orleans.c | 212 ++++++++++++++++++++++++++++++++++++ src/cookie_iterator.c | 148 +++++++++++++++++++++++++ src/test.c | 31 ++++++ 9 files changed, 486 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 include/auth_univ_orleans.h create mode 100644 include/cookie_iterator.h create mode 100644 src/auth_univ_orleans.c create mode 100644 src/cookie_iterator.c create mode 100644 src/test.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..39f9642 --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +IndentWidth: 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a39595f --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +compile_commands.json +.cache +.vscode +bin +lib +obj \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..57f74ad --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +.PHONY: all clean format + +all: lib/libauthunivorleans.so lib/libauthunivorleans.a bin/test + +OPTIMISATION ?= 0 +CFLAGS = -Iinclude $(DEFINES) `pkg-config --cflags libcurl` -fPIC -Wall -Werror + +FLAGS = -g + +SRCS=$(wildcard src/*.c) +OBJS=$(SRCS:src/%.c=obj/%.o) +DEPS = $(OBJS:%.o=%.d) + +-include $(DEPS) + +clean: + rm -fr bin/* obj/* lib/* + +format: + clang-format src/*.c -i + clang-format include/*.h -i + +CC ?= $(CC) + +obj/%.o: src/%.c + @mkdir -p $(@D) + $(CC) -c -O$(OPTIMISATION) -MMD -g -o $@ $< $(CFLAGS) $(FLAGS) + +lib/libauthunivorleans.so: obj/auth_univ_orleans.o obj/cookie_iterator.o + @mkdir -p $(@D) + $(CC) -fpic -shared -O$(OPTIMISATION) $(FLAGS) -flto -o $@ $^ `pkg-config --libs libcurl` `pkg-config --libs tidy` + +lib/libauthunivorleans.a: obj/auth_univ_orleans.o obj/cookie_iterator.o + @mkdir -p $(@D) + $(AR) -cr $@ $^ + +bin/test: obj/test.o lib/libauthunivorleans.a + @mkdir -p $(@D) + $(CC) -g -O$(OPTIMISATION) -flto -o $@ $^ `pkg-config --libs libcurl` `pkg-config --libs tidy` \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..060f5cc --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Auth Univ Orléans \ No newline at end of file diff --git a/include/auth_univ_orleans.h b/include/auth_univ_orleans.h new file mode 100644 index 0000000..b62657e --- /dev/null +++ b/include/auth_univ_orleans.h @@ -0,0 +1,9 @@ +#ifndef AUTH_UNIV_ORLEANS +#define AUTH_UNIV_ORLEANS + +#include +#include + +cookie_iterator_t *auth_univ_get_session_token(char *username, char *password); +int auth_univ_orleans_login(CURL *handle, char *username, char *password); +#endif \ No newline at end of file diff --git a/include/cookie_iterator.h b/include/cookie_iterator.h new file mode 100644 index 0000000..6f54731 --- /dev/null +++ b/include/cookie_iterator.h @@ -0,0 +1,39 @@ +#ifndef COOKIE_ITERATOR_H +#define COOKIE_ITERATOR_H + +#include +#include + +struct cookie_iterator { + struct curl_slist *cookie_list; + struct curl_slist *current; + struct cookie *cookie; +}; + +struct cookie { + char hostname[256]; + bool include_subdomains; + char path[256]; + bool secure; + long expire; + char name[256]; + char value[256]; +}; + +typedef struct cookie_iterator cookie_iterator_t; +typedef struct cookie cookie_t; + +cookie_iterator_t *new_cookie_iterator_from_curl(CURL *handle); +cookie_t *cookie_iterator_next(cookie_iterator_t *iterator); +void cookie_iterator_free(cookie_iterator_t *iterator); + +char *cookie_hostname(cookie_t *cookie); +bool cookie_include_subdomains(cookie_t *cookie); +char *cookie_path(cookie_t *cookie); +bool cookie_secure(cookie_t *cookie); +int cookie_expire(cookie_t *cookie); +char *cookie_name(cookie_t *cookie); +char *cookie_value(cookie_t *cookie); +void cookie_free(cookie_t *cookie); + +#endif \ No newline at end of file diff --git a/src/auth_univ_orleans.c b/src/auth_univ_orleans.c new file mode 100644 index 0000000..b5a7767 --- /dev/null +++ b/src/auth_univ_orleans.c @@ -0,0 +1,212 @@ +#include +#include +#include +#include +#include +#include +#include + +int private_auth_univ_orleans_post_login(CURL *handle, char *lt, char *action, + char *username, char *password); + +int private_auth_univ_orleans_get_action_and_lt(CURL *handle, char **lt, + char **action); + +size_t private_auth_univ_orleans_dumb_write_callback(void *data, size_t size, + size_t nmemb, + void *userp) { + return size * nmemb; +} + +uint private_auth_univ_orleans_tidy_buf_write_cb(char *in, uint size, + uint nmemb, TidyBuffer *out) { + uint r; + r = size * nmemb; + tidyBufAppend(out, in, r); + return r; +} + +int private_auth_univ_orleans_get_action_and_lt_from_node(TidyDoc doc, + TidyNode tnod, + char **lt, + char **action); + +cookie_iterator_t *auth_univ_get_session_token(char *username, char *password) { + CURL *handle = curl_easy_init(); + char errbuf[CURL_ERROR_SIZE]; + + curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errbuf); + + /* set the error buffer as empty before performing a request */ + errbuf[0] = 0; + + // curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1L); + + auth_univ_orleans_login(handle, username, password); + + cookie_iterator_t *list = new_cookie_iterator_from_curl(handle); + + curl_easy_cleanup(handle); + + return list; +} + +int auth_univ_orleans_login(CURL *handle, char *username, char *password) { + curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(handle, CURLOPT_COOKIEFILE, ""); + curl_easy_setopt(handle, CURLOPT_USERAGENT, + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:93.0) " + "Gecko/20100101 Firefox/93.0"); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, + private_auth_univ_orleans_dumb_write_callback); + + curl_easy_setopt(handle, CURLOPT_TCP_KEEPALIVE, 1L); + + curl_easy_setopt(handle, CURLOPT_URL, "https://ent.univ-orleans.fr/"); + + curl_easy_perform(handle); + + char *lt = NULL; + char *action = NULL; + private_auth_univ_orleans_get_action_and_lt(handle, <, &action); + + if (lt == NULL || action == NULL) { + return 0; + } + + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, + private_auth_univ_orleans_dumb_write_callback); + + private_auth_univ_orleans_post_login(handle, lt, action, username, + password); + + free(lt); + free(action); + + return 0; +} + +int private_auth_univ_orleans_get_action_and_lt(CURL *handle, char **lt, + char **action) { + TidyDoc tdoc; + TidyBuffer docbuf = {0}; + // TidyBuffer tidy_errbuf = {0}; + + int err; + curl_easy_setopt( + handle, CURLOPT_URL, + "https://auth.univ-orleans.fr/cas/login?service=https://" + "ent.univ-orleans.fr/uPortal/" + "Login%3FrefUrl%3D%2FuPortal%2Ff%2Faccueil%2Fnormal%2Frender.uPn"); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, + private_auth_univ_orleans_tidy_buf_write_cb); + + tdoc = tidyCreate(); + tidyOptSetBool(tdoc, TidyShowWarnings, no); + tidyOptSetInt(tdoc, TidyWrapLen, 4096); + tidyBufInit(&docbuf); + tidyOptSetBool(tdoc, TidyQuiet, 1L); + + curl_easy_setopt(handle, CURLOPT_WRITEDATA, &docbuf); + + err = curl_easy_perform(handle); + if (!err) { + err = tidyParseBuffer(tdoc, &docbuf); /* parse the input */ + if (err >= 0) { + private_auth_univ_orleans_get_action_and_lt_from_node( + tdoc, tidyGetRoot(tdoc), lt, action); /* walk the tree */ + } + } + + tidyBufFree(&docbuf); + // tidyBufFree(&tidy_errbuf); + tidyRelease(tdoc); + return 0; +} + +int private_auth_univ_orleans_post_login(CURL *handle, char *lt, char *action, + char *username, char *password) { + char form_data[256]; + char *encoded_lt = curl_easy_escape(handle, lt, strlen(lt)); + char *encoded_username = + curl_easy_escape(handle, username, strlen(username)); + char *encoded_password = + curl_easy_escape(handle, password, strlen(password)); + int nbr = snprintf( + form_data, 256, + "lt=%s&username=%s&password=%s&_eventId=submit&submit=SE+CONNECTER", + encoded_lt, encoded_username, encoded_password); + + curl_easy_setopt(handle, CURLOPT_POSTFIELDS, form_data); + curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, nbr); + + curl_easy_setopt(handle, CURLOPT_POST, 1L); + + int action_size = strlen(action); + char *url = (char *)calloc(28 + action_size + 1, sizeof(char)); + + strncpy(url, "https://auth.univ-orleans.fr", 29); + strncpy(url + 28, action, action_size); + + curl_easy_setopt(handle, CURLOPT_URL, url); + + curl_easy_perform(handle); + + free(url); + + curl_free(encoded_lt); + curl_free(encoded_username); + curl_free(encoded_password); + + return 0; +} + +int private_auth_univ_orleans_get_action_and_lt_from_node(TidyDoc doc, + TidyNode tnod, + char **lt, + char **action) { + TidyNode child; + for (child = tidyGetChild(tnod); child; child = tidyGetNext(child)) { + ctmbstr name = tidyNodeGetName(child); + if (name) { + TidyAttr attr; + int is_lt = 0; + int is_action = 0; + const char *value = NULL; + + for (attr = tidyAttrFirst(child); attr; attr = tidyAttrNext(attr)) { + if (!is_lt && !is_action && + strcmp(tidyAttrName(attr), "name") == 0 && + strcmp(tidyAttrValue(attr), "lt") == 0) // TODO OPTIMISE + { + is_lt = 1; + } else if (!is_action && !is_lt && + strcmp(tidyAttrName(attr), "id") == 0 && + strcmp(tidyAttrValue(attr), "fm1") == + 0) // TODO OPTIOMISE + { + is_action = 1; + } else if (strcmp(tidyAttrName(attr), "value") == 0) { + value = tidyAttrValue(attr); + } else if (!is_lt && + strcmp(tidyAttrName(attr), "action") == 0) { + value = tidyAttrValue(attr); + } + } + + if (is_lt && value != NULL) { + *lt = strdup(value); + } else if (is_action && value != NULL) { + *action = strdup(value); + } + } + + if (*lt == NULL || *action == NULL) { + if (private_auth_univ_orleans_get_action_and_lt_from_node( + doc, child, lt, action)) { + return 1; + } + } + } + return *lt != NULL && *action != NULL; +} diff --git a/src/cookie_iterator.c b/src/cookie_iterator.c new file mode 100644 index 0000000..8873c48 --- /dev/null +++ b/src/cookie_iterator.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include + +#define STR_BUF_SIZE 256 + +cookie_iterator_t *new_cookie_iterator_from_curl(CURL *handle) { + struct cookie_iterator *iterator = + (struct cookie_iterator *)calloc(1, sizeof(struct cookie_iterator)); + iterator->cookie_list = NULL; + int res = + curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &iterator->cookie_list); + + if (!res && iterator->cookie_list) { + iterator->current = iterator->cookie_list; + iterator->cookie = (struct cookie *)calloc(1, sizeof(struct cookie)); + + return iterator; + } else { + return NULL; + } +} + +cookie_t *cookie_iterator_next(cookie_iterator_t *iterator) { + if (iterator->cookie_list == NULL) { + return NULL; + } + + if (iterator->current != NULL) { + char *data = iterator->current->data; + char buf[STR_BUF_SIZE]; + + int offset = 0; + + char c; + int i = 0; + while ((c = data[i]) && c != '\t' && + i < sizeof(iterator->cookie->hostname) - 1) { + iterator->cookie->hostname[i] = c; + i++; + } + iterator->cookie->hostname[i] = '\0'; + i++; + offset += i; + + i = 0; + while ((c = data[offset + i]) && c != '\t' && i < STR_BUF_SIZE) { + buf[i] = c; + i++; + } + buf[i] = '\0'; + i++; + offset += i; + + if (strncmp(buf, "TRUE", 4) == 0) { + iterator->cookie->include_subdomains = 1; + } else { + iterator->cookie->include_subdomains = 0; + } + + i = 0; + while ((c = data[offset + i]) && c != '\t' && + i < sizeof(iterator->cookie->path) - 1) { + iterator->cookie->path[i] = c; + i++; + } + iterator->cookie->path[i] = '\0'; + i++; + offset += i; + + i = 0; + while ((c = data[offset + i]) && c != '\t' && i < STR_BUF_SIZE) { + buf[i] = c; + i++; + } + buf[i] = '\0'; + i++; + offset += i; + + if (strncmp(buf, "TRUE", 4) == 0) { + iterator->cookie->secure = 1; + } else { + iterator->cookie->secure = 0; + } + + i = 0; + while ((c = data[offset + i]) && c != '\t' && i < STR_BUF_SIZE) { + buf[i] = c; + i++; + } + buf[i] = '\0'; + i++; + offset += i; + + iterator->cookie->expire = atol(buf); + + i = 0; + while ((c = data[offset + i]) && c != '\t' && + i < sizeof(iterator->cookie->name) - 1) { + iterator->cookie->name[i] = c; + i++; + } + iterator->cookie->name[i] = '\0'; + i++; + offset += i; + + i = 0; + while ((c = data[offset + i]) && c != '\t' && + i < sizeof(iterator->cookie->value) - 1) { + iterator->cookie->value[i] = c; + i++; + } + iterator->cookie->value[i] = '\0'; + + iterator->current = iterator->current->next; + } + + return (iterator->current != NULL) ? iterator->cookie : NULL; +} + +void cookie_iterator_free(cookie_iterator_t *iterator) { + if (iterator->cookie_list != NULL) { + curl_slist_free_all(iterator->cookie_list); + iterator->cookie_list = NULL; + } + + if (iterator->cookie != NULL) { + cookie_free(iterator->cookie); + iterator->cookie = NULL; + } + + iterator->current = NULL; + + free(iterator); +} + +char *cookie_hostname(cookie_t *cookie) { return cookie->hostname; } +bool cookie_include_subdomains(cookie_t *cookie) { + return cookie->include_subdomains; +} +char *cookie_path(cookie_t *cookie) { return cookie->path; } +bool cookie_secure(cookie_t *cookie) { return cookie->secure; } +int cookie_expire(cookie_t *cookie) { return cookie->expire; } +char *cookie_name(cookie_t *cookie) { return cookie->name; } +char *cookie_value(cookie_t *cookie) { return cookie->value; } + +void cookie_free(cookie_t *cookie) { free(cookie); } \ No newline at end of file diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..d74e2e0 --- /dev/null +++ b/src/test.c @@ -0,0 +1,31 @@ +#include +#include +#include + +int main(void) { + char *username = getenv("USERNAME"); + char *password = getenv("PASSWORD"); + + if (username == NULL || password == NULL) { + fprintf(stderr, "ERROR : missing USERNAME or PASSWORD env var\n"); + return -1; + } + + cookie_iterator_t *iter = auth_univ_get_session_token(username, password); + cookie_t *cookie; + + while ((cookie = cookie_iterator_next(iter))) { + printf("%s :\n" + "\thostname: %s\n" + "\tinclude subdomains : %d\n" + "\tpath : %s\n" + "\tsecure : %d\n" + "\texpire : %d\n" + "\tvalue: %s\n", + cookie_name(cookie), cookie_hostname(cookie), + cookie_include_subdomains(cookie), cookie_path(cookie), + cookie_secure(cookie), cookie_expire(cookie), cookie_value(cookie)); + } + + cookie_iterator_free(iter); +} \ No newline at end of file