From e3fd7cddf73974770ddb4a82fe755d4760ae0d5d Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Mon, 16 Nov 2015 20:44:22 +0100 Subject: machinemode: fix bugs and increase readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/slibc-human/machinemode.c | 239 ++++++++++++++++++++++++++++++------------ 1 file changed, 171 insertions(+), 68 deletions(-) diff --git a/src/slibc-human/machinemode.c b/src/slibc-human/machinemode.c index 06029d3..f454613 100644 --- a/src/slibc-human/machinemode.c +++ b/src/slibc-human/machinemode.c @@ -23,6 +23,173 @@ +/** + * Owner can execute/list or/and set UID. + */ +#define S_ISUSR (S_ISUID | S_IXUSR) + +/** + * Group can execute/list or/and set GID. + */ +#define S_ISGRP (S_ISGID | S_IXGRO) + +/** + * Others (not in group) can execute/list or/and sticky bit. + */ +#define S_ISOTH (S_ISVTX | S_IXOTH) + + +/** + * Store permission bits. + * + * @param V:mode_t The variables for the permissions. + * @param U:int type First-level index in `bits` for the user. + * @param T:int type Second-level index in `bits` for bits to verify are unset. + * @param S:int type Second-level index in `bits` for bits to set. + */ +#define BITS_(V, U, T, S) if ((V) & bits[U][T]) goto invalid; else V |= bits[U][S] + +/** + * Store permission bits. + * + * @parma S:char Character representing the bits to set. + * @param V:mode_t The variables for the permissions. + * @param U:int type First-level index in `bits` for the user. + */ +#define BITS(S, V, U) \ + if ((S) == 'r') { BITS_(V, U, 0, 0); } \ + else if ((S) == 'w') { BITS_(V, U, 1, 1); } \ + else if ((S) == 'x') { BITS_(V, U, 3, 2); } \ + else if ((S) == 's') { BITS_(V, U, 3, 3); } \ + else if ((S) == 't') { BITS_(V, U, 3, 3); } \ + else if ((S) == 'S') { BITS_(V, U, 3, 4); } \ + else if ((S) == 'T') { BITS_(V, U, 3, 4); } \ + else if ((S) != '-') goto invalid + + + +/** + * User {0=owner. 1=group, 2=others} to permissions {0=can read, + * 1=can write, 2=can execute/list, 3=can execute/list or/and + * special, 4=special} to permissions bits map. + */ +static mode_t bits[][] = { + {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUSR, S_ISUID}, + {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGRP, S_ISGID}, + {S_IROTH, S_IWOTH, S_IXOTH, S_ISOTH, S_ISVTX}, +}; + + +/** + * Parse a symbolic file permission string with partial update. + * + * @param mode Output parameter for the bits to set, may be `NULL`. + * @param mask Output parameter for the bits to update, may be `NULL`. + * @param str The file permission to parse, must not include file type or be `NULL`. + * @return Zero on success, -1 on error. + * + * @throws EINVAL If `str` is not parseable. + */ +static inline int partial_symbolic(mode_t* restrict mode, mode_t* restrict mask, const char* restrict str) +{ +#define TEST_(S, T, V) (strstarts(str, S) && !(T & (symbols = V))) +#define TEST(S, T) (TEST_(S"+", T, 1) || TEST_(S"-", T, 2) || TEST_(S"=", T, 3)) +#define TESTV(T) (TEST(#T, T) ? (T |= symbols) : 0) + + int u = 0, g = 0, o = 0, symbols, first, user, last; + mode_t or = 0, andn = 0, partial_or; + char symbol; + + for (; *str; str++) + { + /* Check a/u/g/o and =/+/-. */ + if (TEST("a", u|g|o)) first = 3, u |= symbols, g |= symbols, o |= symbols; + else if (TESTV(u)) first = 0; + else if (TESTV(g)) first = 1; + else if (TESTV(o)) first = 2; + else + goto invalid; + + /* Get range of effected users. */ + if (first < 3) last = first + 1; + else last = first, first = 0; + + /* Get =/+/- and jump to permissions. */ + symbol = str[1], str += 2; + + /* Get permissions. */ + for (partial_or = 0; *str && (*str != ','); str++) + for (user = first; user < last; user++) + BITS(*str, partial_or, j); + + /* Apply permissions. */ + if (symbol != '-') or |= partial_or; + if (symbol != '=') andn |= partial_or; + else + for (user = first; user < last; user++) + andn |= bits[user][0] | bits[user][1] | bits[user][3]; + } + + if (mode) *mode = or; + if (mask) *mask = andn; + return 0; + invalid: + return errno = EINVAL, -1; +} + + +/** + * Parse a full symbolic file permission string. + * + * @param mode Output parameter for the bits to set, may be `NULL`. + * @param mask Output parameter for the bits to update, may be `NULL`. + * @param str The file permission to parse, must not include file type or be `NULL`. + * @return Zero on success, -1 on error. + * + * @throws EINVAL If `str` is not parseable. + */ +static inline int exact_symbolic(mode_t* restrict mode, mode_t* restrict mask, const char* restrict str) +{ + mode_t or = 0; + long int user, perm; + + for (user = 0; user < 3; user++) + for (perm = 0; perm < 3; perm++, str++) + BITS(*str, or, user); + + if (mode) *mode = or; + if (mask) *mask = 07777; + return 0; + invalid: + return errno = EINVAL, -1; +} + + +/** + * Parse a numeric file permission string. + * + * @param mode Output parameter for the bits to set, may be `NULL`. + * @param mask Output parameter for the bits to update, may be `NULL`. + * @param str The file permission to parse, must not include file type or be `NULL`. + * @return Zero on success, -1 on error. + * + * @throws EINVAL If `str` is not parseable. + */ +static inline int exact_numeric(mode_t* restrict mode, mode_t* restrict mask, const char* restrict str) +{ + mode_t or = 0; + char s; + + for (; (s = *str); str++) + if (('0' > s) || (s > '7') || (or = (or << 3) | (s & 15), or > 07777)) + return errno = EINVAL, -1; + + if (mode) *mode = or; + if (mask) *mask = 07777; + return 0; +} + + /** * Parses a human representation of file permissions, and updates to file permissions. * @@ -40,84 +207,20 @@ */ int machinemode(mode_t* restrict mode, mode_t* restrict mask, const char* restrict str) { -#define S_ISUSR (S_ISUID | S_IXUSR) -#define S_ISGRP (S_ISGID | S_IXGRO) -#define S_ISOTH (S_ISVTX | S_IXOTH) - -#define TEST_(S, T, V) (strstarts(str, S) && !(T & (v = V))) -#define TEST(S, T) (TEST_(S"+", T, 1) || TEST_(S"-", T, 2) || TEST_(S"=", T, 3)) -#define TESTV(T) (TEST(#T, T) ? (T = v) : 0) - -#define BITS_(V, T, S) if (V & bits[i][T]) goto invalid; else V |= bits[i][S] -#define BITS(V) \ - if (*str == 'r') { BITS_(V, 0, 0); } \ - else if (*str == 'w') { BITS_(V, 1, 1); } \ - else if (*str == 'x') { BITS_(V, 3, 2); } \ - else if (*str == 's') { BITS_(V, 3, 3); } \ - else if (*str == 't') { BITS_(V, 3, 3); } \ - else if (*str == 'S') { BITS_(V, 3, 4); } \ - else if (*str == 'T') { BITS_(V, 3, 4); } \ - else if (*str != '-') goto invalid - - int i, j, n, u = 0, g = 0, o = 0, v; - char s; - mode_t or = 0, andn = 0, part; - mode_t bits[][] = { - {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUSR, S_ISUID}, - {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGRP, S_ISGID}, - {S_IROTH, S_IWOTH, S_IXOTH, S_ISOTH, S_ISVTX}, - }; - switch (*str) { case '\0': - goto invalid; + return errno = EINVAL, -1; case 'a': case 'u': case 'g': case 'o': - /* Partial, symbolic. */ - for (; *str; str++) - { - if (TEST("a", u|g|o)) i = 3, u |= v, g |= v, o |= v; - else if (TESTV(u)) i = 0; - else if (TESTV(g)) i = 1; - else if (TESTV(o)) i = 2; - else - goto invalid; - if (n < 3) n = i + 1; - else n = i, i = 0; - s = str[1], str += 2; - for (part = 0; *str && (*str != ','); str++) - for (j = i; j < n; j++) - BITS(part); - if (s != '-') or |= part; - if (s != '=') andn |= part; - else - for (j = i; j < n; j++) - andn |= bits[i][0] | bits[i][1] | bits[i][3]; - } - break; + return partial_symbolic(mode, mask, str); case 'r': case 'w': case 'x': case '-': case 's': case 'S': case 't': case 'T': - /* Exact, symbolic. */ - for (andn = 07777, i = 0; i < 3; i++) - for (j = 0; j < 3; j++, str++) - BITS(or); - break; + return exact_symbolic(mode, mask, str); default: - /* Exact, numeric. */ - for (andn = 07777; (s = *str); str++) - if (('0' > s) || (s > '7') || (or = (or << 3) | (s & 15), or > 07777)) - goto invalid; - break; + return exact_numeric(mode, mask, str); } - - if (mode) *mode = or; - if (mask) *mask = andn; - return 0; - - invalid: - return errno = EINVAL, -1; } -- cgit v1.2.3-70-g09d2