You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
4.5 KiB
172 lines
4.5 KiB
Treat an ID of -1 as invalid since that means "no change". |
|
Fixes CVE-2019-14287. |
|
Found by Joe Vennix from Apple Information Security. |
|
|
|
diff -r fcd7a6d8330e lib/util/strtoid.c |
|
--- a/lib/util/strtoid.c Fri Jan 11 13:31:15 2019 -0700 |
|
+++ b/lib/util/strtoid.c Thu Oct 10 09:52:12 2019 -0600 |
|
@@ -1,5 +1,5 @@ |
|
/* |
|
- * Copyright (c) 2013-2016 Todd C. Miller <Todd.Miller@sudo.ws> |
|
+ * Copyright (c) 2013-2019 Todd C. Miller <Todd.Miller@sudo.ws> |
|
* |
|
* Permission to use, copy, modify, and distribute this software for any |
|
* purpose with or without fee is hereby granted, provided that the above |
|
@@ -47,6 +47,27 @@ |
|
#include "sudo_util.h" |
|
|
|
/* |
|
+ * Make sure that the ID ends with a valid separator char. |
|
+ */ |
|
+static bool |
|
+valid_separator(const char *p, const char *ep, const char *sep) |
|
+{ |
|
+ bool valid = false; |
|
+ debug_decl(valid_separator, SUDO_DEBUG_UTIL) |
|
+ |
|
+ if (ep != p) { |
|
+ /* check for valid separator (including '\0') */ |
|
+ if (sep == NULL) |
|
+ sep = ""; |
|
+ do { |
|
+ if (*ep == *sep) |
|
+ valid = true; |
|
+ } while (*sep++ != '\0'); |
|
+ } |
|
+ debug_return_bool(valid); |
|
+} |
|
+ |
|
+/* |
|
* Parse a uid/gid in string form. |
|
* If sep is non-NULL, it contains valid separator characters (e.g. comma, space) |
|
* If endp is non-NULL it is set to the next char after the ID. |
|
@@ -60,38 +81,35 @@ sudo_strtoid_v1(const char *p, const cha |
|
char *ep; |
|
id_t ret = 0; |
|
long long llval; |
|
- bool valid = false; |
|
debug_decl(sudo_strtoid, SUDO_DEBUG_UTIL) |
|
|
|
/* skip leading space so we can pick up the sign, if any */ |
|
while (isspace((unsigned char)*p)) |
|
p++; |
|
- if (sep == NULL) |
|
- sep = ""; |
|
+ |
|
+ /* While id_t may be 64-bit signed, uid_t and gid_t are 32-bit unsigned. */ |
|
errno = 0; |
|
llval = strtoll(p, &ep, 10); |
|
- if (ep != p) { |
|
- /* check for valid separator (including '\0') */ |
|
- do { |
|
- if (*ep == *sep) |
|
- valid = true; |
|
- } while (*sep++ != '\0'); |
|
+ if ((errno == ERANGE && llval == LLONG_MAX) || llval > (id_t)UINT_MAX) { |
|
+ errno = ERANGE; |
|
+ if (errstr != NULL) |
|
+ *errstr = N_("value too large"); |
|
+ goto done; |
|
} |
|
- if (!valid) { |
|
+ if ((errno == ERANGE && llval == LLONG_MIN) || llval < INT_MIN) { |
|
+ errno = ERANGE; |
|
+ if (errstr != NULL) |
|
+ *errstr = N_("value too small"); |
|
+ goto done; |
|
+ } |
|
+ |
|
+ /* Disallow id -1, which means "no change". */ |
|
+ if (!valid_separator(p, ep, sep) || llval == -1 || llval == (id_t)UINT_MAX) { |
|
if (errstr != NULL) |
|
*errstr = N_("invalid value"); |
|
errno = EINVAL; |
|
goto done; |
|
} |
|
- if (errno == ERANGE) { |
|
- if (errstr != NULL) { |
|
- if (llval == LLONG_MAX) |
|
- *errstr = N_("value too large"); |
|
- else |
|
- *errstr = N_("value too small"); |
|
- } |
|
- goto done; |
|
- } |
|
ret = (id_t)llval; |
|
if (errstr != NULL) |
|
*errstr = NULL; |
|
@@ -106,30 +124,15 @@ sudo_strtoid_v1(const char *p, const cha |
|
{ |
|
char *ep; |
|
id_t ret = 0; |
|
- bool valid = false; |
|
debug_decl(sudo_strtoid, SUDO_DEBUG_UTIL) |
|
|
|
/* skip leading space so we can pick up the sign, if any */ |
|
while (isspace((unsigned char)*p)) |
|
p++; |
|
- if (sep == NULL) |
|
- sep = ""; |
|
+ |
|
errno = 0; |
|
if (*p == '-') { |
|
long lval = strtol(p, &ep, 10); |
|
- if (ep != p) { |
|
- /* check for valid separator (including '\0') */ |
|
- do { |
|
- if (*ep == *sep) |
|
- valid = true; |
|
- } while (*sep++ != '\0'); |
|
- } |
|
- if (!valid) { |
|
- if (errstr != NULL) |
|
- *errstr = N_("invalid value"); |
|
- errno = EINVAL; |
|
- goto done; |
|
- } |
|
if ((errno == ERANGE && lval == LONG_MAX) || lval > INT_MAX) { |
|
errno = ERANGE; |
|
if (errstr != NULL) |
|
@@ -142,28 +145,31 @@ sudo_strtoid_v1(const char *p, const cha |
|
*errstr = N_("value too small"); |
|
goto done; |
|
} |
|
- ret = (id_t)lval; |
|
- } else { |
|
- unsigned long ulval = strtoul(p, &ep, 10); |
|
- if (ep != p) { |
|
- /* check for valid separator (including '\0') */ |
|
- do { |
|
- if (*ep == *sep) |
|
- valid = true; |
|
- } while (*sep++ != '\0'); |
|
- } |
|
- if (!valid) { |
|
+ |
|
+ /* Disallow id -1, which means "no change". */ |
|
+ if (!valid_separator(p, ep, sep) || lval == -1) { |
|
if (errstr != NULL) |
|
*errstr = N_("invalid value"); |
|
errno = EINVAL; |
|
goto done; |
|
} |
|
+ ret = (id_t)lval; |
|
+ } else { |
|
+ unsigned long ulval = strtoul(p, &ep, 10); |
|
if ((errno == ERANGE && ulval == ULONG_MAX) || ulval > UINT_MAX) { |
|
errno = ERANGE; |
|
if (errstr != NULL) |
|
*errstr = N_("value too large"); |
|
goto done; |
|
} |
|
+ |
|
+ /* Disallow id -1, which means "no change". */ |
|
+ if (!valid_separator(p, ep, sep) || ulval == UINT_MAX) { |
|
+ if (errstr != NULL) |
|
+ *errstr = N_("invalid value"); |
|
+ errno = EINVAL; |
|
+ goto done; |
|
+ } |
|
ret = (id_t)ulval; |
|
} |
|
if (errstr != NULL)
|
|
|