1
0
mirror of https://github.com/osmarks/ngircd.git synced 2025-02-24 05:00:01 +00:00

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.
This commit is contained in:
Federico G. Schwindt 2013-04-19 23:51:42 +01:00
parent 528c8fc244
commit cde2e8a277
4 changed files with 35 additions and 34 deletions

View File

@ -33,8 +33,6 @@
struct list_head My_Classes[CLASS_COUNT]; struct list_head My_Classes[CLASS_COUNT];
char Reject_Reason[COMMAND_LEN];
GLOBAL void GLOBAL void
Class_Init(void) Class_Init(void)
{ {
@ -49,32 +47,29 @@ Class_Exit(void)
for (i = 0; i < CLASS_COUNT; Lists_Free(&My_Classes[i++])); for (i = 0; i < CLASS_COUNT; Lists_Free(&My_Classes[i++]));
} }
GLOBAL char * GLOBAL bool
Class_GetMemberReason(const int Class, CLIENT *Client) Class_GetMemberReason(const int Class, CLIENT *Client, char *reason, size_t len)
{ {
char *reason; char str[COMMAND_LEN] = "listed";
assert(Class < CLASS_COUNT); assert(Class < CLASS_COUNT);
assert(Client != NULL); assert(Client != NULL);
reason = Lists_CheckReason(&My_Classes[Class], Client); if (!Lists_CheckReason(&My_Classes[Class], Client, str, sizeof(str)))
if (!reason) return false;
return NULL;
if (!*reason)
reason = "listed";
switch(Class) { switch(Class) {
case CLASS_GLINE: case CLASS_GLINE:
snprintf(Reject_Reason, sizeof(Reject_Reason), snprintf(reason, len, "\"%s\" (G-Line)", str);
"\"%s\" (G-Line)", reason); break;
return Reject_Reason;
case CLASS_KLINE: case CLASS_KLINE:
snprintf(Reject_Reason, sizeof(Reject_Reason), snprintf(reason, len, "\"%s\" (K-Line)", str);
"\"%s\" (K-Line)", reason); break;
return Reject_Reason; default:
snprintf(reason, len, "%s", str);
break;
} }
return reason; return true;
} }
/** /**
@ -88,15 +83,13 @@ Class_GetMemberReason(const int Class, CLIENT *Client)
GLOBAL bool GLOBAL bool
Class_HandleServerBans(CLIENT *Client) Class_HandleServerBans(CLIENT *Client)
{ {
char *rejectptr; char reject[COMMAND_LEN];
assert(Client != NULL); assert(Client != NULL);
rejectptr = Class_GetMemberReason(CLASS_GLINE, Client); if (Class_GetMemberReason(CLASS_GLINE, Client, reject, sizeof(reject)) ||
if (!rejectptr) Class_GetMemberReason(CLASS_KLINE, Client, reject, sizeof(reject))) {
rejectptr = Class_GetMemberReason(CLASS_KLINE, Client); Client_Reject(Client, reject, true);
if (rejectptr) {
Client_Reject(Client, rejectptr, true);
return DISCONNECTED; return DISCONNECTED;
} }

View File

@ -29,7 +29,8 @@ GLOBAL bool Class_AddMask PARAMS((const int Class, const char *Mask,
const time_t ValidUntil, const char *Reason)); const time_t ValidUntil, const char *Reason));
GLOBAL void Class_DeleteMask PARAMS((const int Class, const char *Mask)); 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 bool Class_HandleServerBans PARAMS((CLIENT *Client));
GLOBAL struct list_head *Class_GetList PARAMS((const int Class)); GLOBAL struct list_head *Class_GetList PARAMS((const int Class));

View File

@ -130,7 +130,8 @@ Lists_Add(struct list_head *h, const char *Mask, time_t ValidUntil,
if (e) { if (e) {
e->valid_until = ValidUntil; e->valid_until = ValidUntil;
if (Reason) { if (Reason) {
free(e->reason); if (e->reason)
free(e->reason);
e->reason = strdup(Reason); e->reason = strdup(Reason);
} }
return true; return true;
@ -320,18 +321,21 @@ Lists_MakeMask(const char *Pattern)
bool bool
Lists_Check(struct list_head *h, CLIENT *Client) 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 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. * @return true if client is listed, false if not.
*/ */
char * bool
Lists_CheckReason(struct list_head *h, CLIENT *Client) Lists_CheckReason(struct list_head *h, CLIENT *Client, char *reason, size_t len)
{ {
struct list_elem *e, *last, *next; struct list_elem *e, *last, *next;
@ -343,19 +347,21 @@ Lists_CheckReason(struct list_head *h, CLIENT *Client)
while (e) { while (e) {
next = e->next; next = e->next;
if (Match(e->mask, Client_MaskCloaked(Client))) { if (Match(e->mask, Client_MaskCloaked(Client))) {
if (len && e->reason)
strlcpy(reason, e->reason, len);
if (e->valid_until == 1) { if (e->valid_until == 1) {
/* Entry is valid only once, delete it */ /* Entry is valid only once, delete it */
LogDebug("Deleted \"%s\" from list (used).", LogDebug("Deleted \"%s\" from list (used).",
e->mask); e->mask);
Lists_Unlink(h, last, e); Lists_Unlink(h, last, e);
} }
return e->reason ? e->reason : ""; return true;
} }
last = e; last = e;
e = next; e = next;
} }
return NULL; return false;
} }
/** /**

View File

@ -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 struct list_elem *Lists_GetNext PARAMS((const struct list_elem *));
GLOBAL bool Lists_Check PARAMS((struct list_head *head, CLIENT *client)); 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, GLOBAL struct list_elem *Lists_CheckDupeMask PARAMS((const struct list_head *head,
const char *mask)); const char *mask));