From: Federico G. Schwindt Date: Fri, 19 Apr 2013 22:51:42 +0000 (+0100) Subject: Fix use-after-free on Lists_CheckReason() X-Git-Tag: rel-21-rc1~105 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=commitdiff_plain;h=cde2e8a2775e8b01266627a60a08e2560eac42c8 Fix use-after-free on Lists_CheckReason() Change Lists_CheckReason() to receive a buffer where the reason will be stored and its length. Change callers accordingly. Change Class_GetMemberReason() (and its callers) in a similar way so it doesn't rely on a global buffer for the rejected reason. --- diff --git a/src/ngircd/class.c b/src/ngircd/class.c index 0f617b82..800e9935 100644 --- a/src/ngircd/class.c +++ b/src/ngircd/class.c @@ -33,8 +33,6 @@ struct list_head My_Classes[CLASS_COUNT]; -char Reject_Reason[COMMAND_LEN]; - GLOBAL void Class_Init(void) { @@ -49,32 +47,29 @@ Class_Exit(void) for (i = 0; i < CLASS_COUNT; Lists_Free(&My_Classes[i++])); } -GLOBAL char * -Class_GetMemberReason(const int Class, CLIENT *Client) +GLOBAL bool +Class_GetMemberReason(const int Class, CLIENT *Client, char *reason, size_t len) { - char *reason; + char str[COMMAND_LEN] = "listed"; assert(Class < CLASS_COUNT); assert(Client != NULL); - reason = Lists_CheckReason(&My_Classes[Class], Client); - if (!reason) - return NULL; - - if (!*reason) - reason = "listed"; + if (!Lists_CheckReason(&My_Classes[Class], Client, str, sizeof(str))) + return false; switch(Class) { case CLASS_GLINE: - snprintf(Reject_Reason, sizeof(Reject_Reason), - "\"%s\" (G-Line)", reason); - return Reject_Reason; + snprintf(reason, len, "\"%s\" (G-Line)", str); + break; case CLASS_KLINE: - snprintf(Reject_Reason, sizeof(Reject_Reason), - "\"%s\" (K-Line)", reason); - return Reject_Reason; + snprintf(reason, len, "\"%s\" (K-Line)", str); + break; + default: + snprintf(reason, len, "%s", str); + break; } - return reason; + return true; } /** @@ -88,15 +83,13 @@ Class_GetMemberReason(const int Class, CLIENT *Client) GLOBAL bool Class_HandleServerBans(CLIENT *Client) { - char *rejectptr; + char reject[COMMAND_LEN]; assert(Client != NULL); - rejectptr = Class_GetMemberReason(CLASS_GLINE, Client); - if (!rejectptr) - rejectptr = Class_GetMemberReason(CLASS_KLINE, Client); - if (rejectptr) { - Client_Reject(Client, rejectptr, true); + if (Class_GetMemberReason(CLASS_GLINE, Client, reject, sizeof(reject)) || + Class_GetMemberReason(CLASS_KLINE, Client, reject, sizeof(reject))) { + Client_Reject(Client, reject, true); return DISCONNECTED; } diff --git a/src/ngircd/class.h b/src/ngircd/class.h index 2a9dbba7..ba2e1606 100644 --- a/src/ngircd/class.h +++ b/src/ngircd/class.h @@ -29,7 +29,8 @@ GLOBAL bool Class_AddMask PARAMS((const int Class, const char *Mask, const time_t ValidUntil, const char *Reason)); GLOBAL void Class_DeleteMask PARAMS((const int Class, const char *Mask)); -GLOBAL char *Class_GetMemberReason PARAMS((const int Class, CLIENT *Client)); +GLOBAL bool Class_GetMemberReason PARAMS((const int Class, CLIENT *Client, + char *reason, size_t len)); GLOBAL bool Class_HandleServerBans PARAMS((CLIENT *Client)); GLOBAL struct list_head *Class_GetList PARAMS((const int Class)); diff --git a/src/ngircd/lists.c b/src/ngircd/lists.c index 21058a03..bab30f22 100644 --- a/src/ngircd/lists.c +++ b/src/ngircd/lists.c @@ -130,7 +130,8 @@ Lists_Add(struct list_head *h, const char *Mask, time_t ValidUntil, if (e) { e->valid_until = ValidUntil; if (Reason) { - free(e->reason); + if (e->reason) + free(e->reason); e->reason = strdup(Reason); } return true; @@ -320,18 +321,21 @@ Lists_MakeMask(const char *Pattern) bool Lists_Check(struct list_head *h, CLIENT *Client) { - return Lists_CheckReason(h, Client) != NULL; + return Lists_CheckReason(h, Client, NULL, 0); } /** - * Check if a client is listed in a list and return the "reason". + * Check if a client is listed in a list and store the reason if a buffer + * is provided. * - * @param h List head. + * @param h List head. * @param Client Client to check. + * @param reason Result buffer to store the reason. + * @param len Size of the buffer. * @return true if client is listed, false if not. */ -char * -Lists_CheckReason(struct list_head *h, CLIENT *Client) +bool +Lists_CheckReason(struct list_head *h, CLIENT *Client, char *reason, size_t len) { struct list_elem *e, *last, *next; @@ -343,19 +347,21 @@ Lists_CheckReason(struct list_head *h, CLIENT *Client) while (e) { next = e->next; if (Match(e->mask, Client_MaskCloaked(Client))) { + if (len && e->reason) + strlcpy(reason, e->reason, len); if (e->valid_until == 1) { /* Entry is valid only once, delete it */ LogDebug("Deleted \"%s\" from list (used).", e->mask); Lists_Unlink(h, last, e); } - return e->reason ? e->reason : ""; + return true; } last = e; e = next; } - return NULL; + return false; } /** diff --git a/src/ngircd/lists.h b/src/ngircd/lists.h index 24504dfa..eb863db9 100644 --- a/src/ngircd/lists.h +++ b/src/ngircd/lists.h @@ -30,7 +30,8 @@ GLOBAL struct list_elem *Lists_GetFirst PARAMS((const struct list_head *)); GLOBAL struct list_elem *Lists_GetNext PARAMS((const struct list_elem *)); GLOBAL bool Lists_Check PARAMS((struct list_head *head, CLIENT *client)); -GLOBAL char *Lists_CheckReason PARAMS((struct list_head *head, CLIENT *client)); +GLOBAL bool Lists_CheckReason PARAMS((struct list_head *head, CLIENT *client, + char *reason, size_t len)); GLOBAL struct list_elem *Lists_CheckDupeMask PARAMS((const struct list_head *head, const char *mask));