📄 mal_authorize.c
字号:
str hash; oid id; rethrow("setPassword", tmp, AUTHrequireAdmin()); /* precondition checks */ if (*username == NULL || strNil(*username)) throw(ILLARG, "setPassword", "username should not be nil"); if (*passwd == NULL || strNil(*passwd)) throw(ILLARG, "setPassword", "password should not be nil"); id = MCgetClient()->user; /* find the name of the administrator and see if it equals username */ p = BUNfnd(user, &id); assert (p != NULL); tmp = BUNtail(user, p); assert (tmp != NULL); if (strcmp(tmp, *username) == 0) throw(INVCRED, "setPassword", "The administrator cannot set its own password, use changePassword instead"); /* see if the user is valid */ p = BUNfnd(BATmirror(user), *username); if (p == NULL) throw(MAL, "setPassword", "no such user '%s'", *username); id = *(oid*)BUNhead(user, p); /* cypher the password */ rethrow("setPassword", tmp, AUTHcypherValue(&hash, passwd)); /* ok, just overwrite the password field for this user */ p = BUNfnd(pass, &id); assert (p != NULL); BUNinplace(pass, p, BUNhead(pass, p), &hash, FALSE); AUTHcommit(); return(MAL_SUCCEED);}/** * Adds the given scenario to the list of allowed scenarios for the * given user. Note that this can result in unexpected behaviour when * there where previously no scenarios defined for the user (which means * all scenarios are permitted). */strAUTHaddScenario(str *username, str *scenario) { BUN p; str tmp; oid *id; BAT *b; rethrow("addScenario", tmp, AUTHrequireAdmin()); /* precondition checks */ if (*username == NULL || strNil(*username)) throw(ILLARG, "addScenario", "username should not be nil"); if (*scenario == NULL || strNil(*scenario)) throw(ILLARG, "addScenario", "scenario should not be nil"); /* see if the user is valid */ p = BUNfnd(BATmirror(user), *username); if (p == NULL) throw(MAL, "addScenario", "user '%s' does not exist", *username); id = (oid*)BUNhead(user, p); /* see if this scenario is not already there */ b = BATselect(BATmirror(scen), id, id); b = BATselect(BATmirror(b), *scenario, *scenario); if (BATcount(b) == 1) throw(MAL, "addScenario", "scenario '%s' already exists for user '%s'", *scenario, *username); if (BATcount(b) > 1) throw(MAL, "addScenario", "inconsistent authorisation administration, scenario '%s' multiple times defined", *scenario); /* add the scenario for this user, use force as sql makes view over it */ BUNins(scen, BUNhead(user, p), *scenario, TRUE); AUTHcommit(); return(MAL_SUCCEED);}/** * Removes the given scenario from the list of allowed scenarios for the * given user. Note that removing the last allowed scenario results in * the opposite effect: it will allow any scenario to be used. */strAUTHremoveScenario(str *username, str *scenario) { BUN p; BAT *b; oid *id; str tmp; rethrow("removeScenario", tmp, AUTHrequireAdmin()); /* precondition checks */ if (*username == NULL || strNil(*username)) throw(ILLARG, "removeScenario", "username should not be nil"); if (*scenario == NULL || strNil(*scenario)) throw(ILLARG, "removeScenario", "scenario should not be nil"); /* see if the user is valid */ p = BUNfnd(BATmirror(user), *username); if (p == NULL) throw(MAL, "removeScenario", "user '%s' does not exist", *username); id = (oid*)BUNhead(user, p); /* see if the scenario is valid for this user */ b = BATselect(BATmirror(scen), id, id); b = BATselect(BATmirror(b), *scenario, *scenario); if (BATcount(b) == 0) throw(MAL, "removeScenario", "scenario '%s' not found for user '%s'", *scenario, *username); if (BATcount(b) > 1) throw(MAL, "removeScenario", "inconsistent authorisation administration, scenario '%s' multiple times defined", *scenario); /* ok, remove it */ BATdel(scen, BATmirror(b), TRUE); AUTHcommit(); return(MAL_SUCCEED);}/** * Resolves the given user id and returns the associated username. If * the id is invalid, an exception is thrown. The given pointer to the * username char buffer should be NULL if this function is supposed to * allocate memory for it. If the pointer is pointing to an already * allocated buffer, it is supposed to be of size BUFSIZ. */strAUTHresolveUser(str *username, oid *uid) { BUN p; if (uid == NULL || *uid == oid_nil) throw(ILLARG, "resolveUser", "userid should not be nil"); p = BUNfnd(user, uid); if (p == NULL) throw(MAL, "resolveUser", "No such user with id: " OIDFMT, *uid); assert (username != NULL); if (*username == NULL) { *username = GDKstrdup((str)(BUNtail(user, p))); } else { snprintf(*username, BUFSIZ, "%s", (str)(BUNtail(user, p))); } return(MAL_SUCCEED);}/** * Returns the username of the current user. */strAUTHgetUsername(str *username) { BUN p; oid id; id = MCgetClient()->user; p = BUNfnd(user, &id); if (p == NULL) { GDKfatal("Internal error: user id that doesn't exist: " OIDFMT, MCgetClient()->user); } *username = BUNtail(user, p); return(MAL_SUCCEED);}/** * Returns a BAT with user names in the tail, and user ids in the head. * Only those users are returned that have access to all of the given * scenarios. */strAUTHgetUsers(bat *ret, bat *scenarios) { BAT *b, *r; str tmp; rethrow("getUsers", tmp, AUTHrequireAdmin()); if (*scenarios != bat_nil) { b = BATdescriptor(*scenarios); if (b == NULL) throw(ILLARG, "getUsers", "invalid BAT!"); if (b->htype != TYPE_str) throw(ILLARG, "getUsers", "BAT should have str head"); if (BATcount(b) == 0) { /* we can simply copy the whole users table, as there is no * selection */ r = BATcopy(user, user->htype, user->ttype, FALSE); } else { BAT *t1; BAT *t2; /* we have to do some work in order to return the requested * rows; only those that have the given scenario(s) (all of * them) */ /* all users with no scenarios are always in the return * list, we find them by "inversing" the list of users * *with* a scenario */ t1 = BATkdiff(user, BATkunique(scen)); /* find users with one or more scenarios in the given BAT */ t2 = BATjoin( VIEWcombine(BATkunique(BATjoin(scen, b, BATcount(scen)))), user, BATcount(user) ); /* the final result is the union of both (we can discard the * tail, as they *should* be equal when the heads are equal * too... */ r = BATkunion(t1, t2); } BBPunfix(*scenarios); } else { r = BATcopy(user, user->htype, user->ttype, FALSE); BBPunfix(*scenarios); } *ret = BBPcacheid(r); return(NULL);}/*=== the vault ===*//* yep, the vault key is just stored in memory */static str vaultKey = NULL;/** * Unlocks the vault with the given password. Since the password is * just the decypher key, it is not possible to directly check whether * the given password is correct. If incorrect, however, all decypher * operations will probably fail or return an incorrect decyphered * value. */strAUTHunlockVault(str *password) { str tmp; rethrow("unlockVault", tmp, AUTHrequireAdmin()); if (password == NULL || strNil(*password)) throw(ILLARG, "unlockVault", "password should not be nil"); /* even though I think this function should be called only once, it * is not of real extra efforts to avoid a mem-leak if it is used * multiple times */ if (vaultKey != NULL) GDKfree(vaultKey); vaultKey = GDKstrdup(*password); return(MAL_SUCCEED);}/** * Decyphers a given value, using the vaultKey. The returned value * might be incorrect if the vaultKey is incorrect or unset. If the * cypher algorithm fails or detects an invalid password, it might throw * an exception. The ret string is GDKmalloced, and should be GDKfreed * by the caller. */static strAUTHdecypherValue(str *ret, str *value) { /* Cyphering and decyphering can be done using many algorithms. * Future requirements might want a stronger cypher than the XOR * cypher chosen here. It is left up to the implementor how to do * that once those algoritms become available. It could be * #ifdef-ed or on if-basis depending on whether the cypher * algorithm is a compile, or runtime option. When necessary, this * function could be extended with an extra argument that indicates * the cypher algorithm. */ /* this is the XOR decypher implementation */ str r = GDKmalloc(sizeof(char) * (strlen(*value) + 1)); str w = r; str s = *value; int escaped = 0; /* we default to some garbage key, just to make password unreadable * (a space would only uppercase the password) */ int keylen = 0; if (vaultKey == NULL) throw(MAL, "decypherValue", "The vault is still locked!"); keylen = strlen(vaultKey); /* XOR all characters. If we encounter a 'one' char after the XOR * operation, it is an escape, so replace it with the next char. */ for (; *s != '\0'; s++) { if (*s == '\1' && escaped == 0) { escaped = 1; continue; } else if (escaped != 0) { *s -= 1; escaped = 0; } *w = *s ^ vaultKey[(w - r) % keylen]; w++; } *w = '\0'; *ret = r; return(MAL_SUCCEED);}/** * Cyphers the given string using the vaultKey. If the cypher algorithm * fails or detects an invalid password, it might throw an exception. * The ret string is GDKmalloced, and should be GDKfreed by the caller. */static strAUTHcypherValue(str *ret, str *value) { /* this is the XOR cypher implementation */ str r = GDKmalloc(sizeof(char) * (strlen(*value) * 2 + 1)); str w = r; str s = *value; /* we default to some garbage key, just to make password unreadable * (a space would only uppercase the password) */ int keylen = 0; if (vaultKey == NULL) throw(MAL, "decypherValue", "The vault is still locked!"); keylen = strlen(vaultKey); /* XOR all characters. If we encounter a 'zero' char after the XOR * operation, escape it with an 'one' char. */ for (; *s != '\0'; s++) { *w = *s ^ vaultKey[(s - *value) % keylen]; if (*w == '\0') { *w++ = '\1'; *w = '\1'; } else if (*w == '\1') { *w++ = '\1'; *w = '\2'; } w++; } *w = '\0'; *ret = r; return(MAL_SUCCEED);}/** * Returns a comma separated list of supported hash algorithms. The * returned string is GDKmalloced and should be GDKfreed. */strAUTHgetHashAlgorithms(str *ret) { /* currently, four "hashes" are available, SHA-1, MD5, crypt and * plain. Both "sha1" and "md5" are supported if OpenSSL is * available at compile time. The same holds for "crypt", the UNIX * crypt implementation. The last option, "plain" is not a hash at * all, but always available as it requires no modifications to the * password, i.e. it is send over in plain text. */ str t = alloca(sizeof(char) * BUFSIZ); *t = '\0';#ifdef HAVE_OPENSSL strcat(t, "SHA1,MD5,");#endif#ifdef HAVE_CRYPT strcat(t, "crypt,");#endif strcat(t, "plain"); *ret = GDKstrdup(t); return(MAL_SUCCEED);}/** * Returns the hash for the given password, challenge and algorithm. * The hash calculated using the given algorithm over the password * concatenated with the challenge. */static strAUTHhashPassword(str *ret, str *algo, str *password, str *challenge) { if (strcmp(*algo, "plain") == 0) { /* The plain text algorithm, doesn't really hash at all. It's * the easiest algorithm, as it just appends the challenge to * the password and returns it. */ *ret = GDKmalloc(sizeof(char) * (strlen(*password) + strlen(*challenge) + 1)); sprintf(*ret, "%s%s", *password, *challenge); return(MAL_SUCCEED);#ifdef HAVE_OPENSSL } else if (strcmp(*algo, "SHA1") == 0) { /* The SHA-1 RSA hash algorithm is a 160 bit hash. In order to * use in a string, a hexadecimal representation of the bit * sequence is used. */ unsigned char md[20]; /* should be SHA_DIGEST_LENGTH */ int len = strlen(*password) + strlen(*challenge); char key[len]; strcpy(key, *password); strncat(key, *challenge, strlen(*challenge)); SHA1((unsigned char*)key, len, md); *ret = GDKmalloc(sizeof(char) * (20 * 2 + 1)); sprintf(*ret, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", md[0], md[1], md[2], md[3], md[4], md[5], md[6], md[7], md[8], md[9], md[10], md[11], md[12], md[13], md[14], md[15], md[16], md[17], md[18], md[19] ); return(MAL_SUCCEED); } else if (strcmp(*algo, "MD5") == 0) { /* The MD5 hash algorithm is a 128 bit hash. In order to * use in a string, a hexadecimal representation of the bit * sequence is used. */ unsigned char md[16]; /* should be MD5_DIGEST_LENGTH */ int len = strlen(*password) + strlen(*challenge); char key[len]; strcpy(key, *password); strncat(key, *challenge, strlen(*challenge)); MD5((unsigned char*)key, len, md); *ret = GDKmalloc(sizeof(char) * (16 * 2 + 1)); sprintf(*ret, "%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x", md[0], md[1], md[2], md[3], md[4], md[5], md[6], md[7], md[8], md[9], md[10], md[11], md[12], md[13], md[14], md[15] ); return(MAL_SUCCEED);#endif#ifdef HAVE_CRYPT } else if (strcmp(*algo, "crypt") == 0) { /* The crypt hash algorithm uses UNIX crypt, a modification of * DES which uses a 2-char wide salt. Because crypt only cares * about the first eight characters of the given password, the * challenge may not be taken into account at all. As salt, the * last two characters of the challenge are used. */ char key[8]; /* NULL termination is not necessary */ char salt[3]; /* NULL termination is a necessity! */ str hash; int len; /* prepare the key */ len = strlen(*password); if (len >= 8) { strncpy(key, *password, 8); } else { /* pad with the challenge, we know it is always 8+ chars */ strncpy(key, *password, len); strncpy(key + len, *challenge, 8 - len); } /* prepare the salt */ len = strlen(*challenge); salt[0] = *challenge[len - 2]; salt[1] = *challenge[len - 1]; salt[2] = '\0'; /* call crypt to do the work */ hash = crypt(key, salt); assert (hash != NULL); *ret = GDKstrdup(hash); return(MAL_SUCCEED);#endif } else { throw(MAL, "hashPassword", "unsupported hash type: '%s'", algo); }}#line 1038 "/export/scratch0/monet/monet.GNU.64.64.d.14791/MonetDB5/src/mal/mal_authorize.mx"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -