Loading test/test.cpp +74 −0 Changes for test/test.cpp: 74 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <cstdlib> #include <cstring> #include <fstream> #include <map> #include <memory> #include <iostream> #include <string> Loading @@ -52,6 +53,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../src/cache.h" #include "../src/config.h" #include "../src/directory.h" #include "../src/discover.h" #include "../src/join.h" #include "../src/gssapi.h" #include "../src/krb5.h" #include "../src/krb5crypto.h" Loading Loading @@ -692,6 +695,75 @@ void testGss() { CHECK(throws([&] { ctx.unwrap(w); }), "unwrap: our own token (not from the acceptor) refused"); } void testDiscover() { /* a DNS answer: two SRV records, the second target compressed */ std::string m; auto u16 = [&](unsigned v) { m += (char)(v >> 8); m += (char)v; }; u16(0x1234); u16(0x8180); u16(1); u16(2); u16(0); u16(0); for (const char *l : { "_ldap", "_tcp", "dc", "_msdcs", "example", "com" }) { m += (char)strlen(l); m += l; } m += '\0'; u16(33); u16(1); auto answer = [&](unsigned prio, unsigned weight, const std::string &target) { u16(0xc00c); u16(33); u16(1); u16(0); u16(600); u16((unsigned)(6 + target.size())); u16(prio); u16(weight); u16(389); m += target; }; answer(0, 100, std::string("\x03" "dc2" "\x07" "example" "\x03" "com", 17) + '\0'); answer(0, 200, std::string("\x03" "dc1", 4) + "\xc0\x21"); std::vector<SrvRecord> r = parseSrv((const unsigned char *)m.data(), m.size()); CHECK(r.size() == 2 && r[0].target == "dc2.example.com" && r[0].port == 389 && r[1].target == "dc1.example.com" && r[1].weight == 200, "SRV answers, with a compressed target"); CHECK(parseSrv((const unsigned char *)m.data(), 20).empty(), "SRV: a truncated answer gives nothing"); } void testJoin(const std::string &dir) { CHECK_EQ(computerName("fenriros-server-01.lan"), std::string("FENRIROS-SERVER"), "computer name: short, upper, 15"); CHECK_EQ(computerName("nas_1"), std::string("NAS1"), "computer name: only letters, digits, -"); fake::Server srv(objects(), BindDn, BindPw); Config admin = testConfig(srv.port()); std::string secret = dir + "/secrets/bind-example.com.secret"; auto utf16 = [](const std::string &s) { std::string q = "\"" + s + "\"", o; for (char c : q) { o += c; o += '\0'; } return o; }; try { JoinResult r = joinWith(admin, "fenrir1.lan", secret, false); CHECK(r.created && r.principal == "FENRIR1$" && r.realm == "EXAMPLE.COM" && r.secretFile == secret, "join: a new account"); fake::Object o; CHECK(srv.object("CN=FENRIR1,CN=Computers,DC=example,DC=com", o), "join: CN=FENRIR1,CN=Computers"); std::map<std::string, std::vector<std::string>> a(o.attrs.begin(), o.attrs.end()); std::ifstream in(secret); std::string pw; std::getline(in, pw); CHECK(pw.size() == 32 && a["unicodePwd"].size() == 1 && a["unicodePwd"][0] == utf16(pw), "join: the password in unicodePwd (quoted, UTF-16LE) and in the secret file"); CHECK(a["sAMAccountName"] == std::vector<std::string>{ "FENRIR1$" } && a["userAccountControl"][0] == "4096" && a["msDS-SupportedEncryptionTypes"][0] == "24" && a["dNSHostName"][0] == "fenrir1.example.com" && a["servicePrincipalName"].size() == 2, "join: the account's attributes"); struct stat st; CHECK(stat(secret.c_str(), &st) == 0 && (st.st_mode & 077) == 0, "join: the secret file root only"); JoinResult again = joinWith(admin, "fenrir1", secret, false); std::ifstream in2(secret); std::string pw2; std::getline(in2, pw2); fake::Object o2; srv.object("CN=FENRIR1,CN=Computers,DC=example,DC=com", o2); std::map<std::string, std::vector<std::string>> a2(o2.attrs.begin(), o2.attrs.end()); CHECK(!again.created && pw2 != pw && a2["unicodePwd"][0] == utf16(pw2), "join again: the existing account, a new password"); } catch (const std::exception &e) { CHECK(false, std::string("join: ") + e.what()); } Config wrong = admin; wrong.bindPassword = "nope"; CHECK(throws([&] { joinWith(wrong, "fenrir2", secret, false); }), "join: a wrong admin password fails"); } time_t fakeNow = 1000; void testCache(const std::string &dir) { Loading Loading @@ -935,6 +1007,8 @@ int main() { testSid(); testConfig(dir); testCache(dir); testDiscover(); testJoin(dir); testKrb5Crypto(); testKeytab(dir); testGss(); Loading Loading
test/test.cpp +74 −0 Changes for test/test.cpp: 74 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <cstdlib> #include <cstring> #include <fstream> #include <map> #include <memory> #include <iostream> #include <string> Loading @@ -52,6 +53,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../src/cache.h" #include "../src/config.h" #include "../src/directory.h" #include "../src/discover.h" #include "../src/join.h" #include "../src/gssapi.h" #include "../src/krb5.h" #include "../src/krb5crypto.h" Loading Loading @@ -692,6 +695,75 @@ void testGss() { CHECK(throws([&] { ctx.unwrap(w); }), "unwrap: our own token (not from the acceptor) refused"); } void testDiscover() { /* a DNS answer: two SRV records, the second target compressed */ std::string m; auto u16 = [&](unsigned v) { m += (char)(v >> 8); m += (char)v; }; u16(0x1234); u16(0x8180); u16(1); u16(2); u16(0); u16(0); for (const char *l : { "_ldap", "_tcp", "dc", "_msdcs", "example", "com" }) { m += (char)strlen(l); m += l; } m += '\0'; u16(33); u16(1); auto answer = [&](unsigned prio, unsigned weight, const std::string &target) { u16(0xc00c); u16(33); u16(1); u16(0); u16(600); u16((unsigned)(6 + target.size())); u16(prio); u16(weight); u16(389); m += target; }; answer(0, 100, std::string("\x03" "dc2" "\x07" "example" "\x03" "com", 17) + '\0'); answer(0, 200, std::string("\x03" "dc1", 4) + "\xc0\x21"); std::vector<SrvRecord> r = parseSrv((const unsigned char *)m.data(), m.size()); CHECK(r.size() == 2 && r[0].target == "dc2.example.com" && r[0].port == 389 && r[1].target == "dc1.example.com" && r[1].weight == 200, "SRV answers, with a compressed target"); CHECK(parseSrv((const unsigned char *)m.data(), 20).empty(), "SRV: a truncated answer gives nothing"); } void testJoin(const std::string &dir) { CHECK_EQ(computerName("fenriros-server-01.lan"), std::string("FENRIROS-SERVER"), "computer name: short, upper, 15"); CHECK_EQ(computerName("nas_1"), std::string("NAS1"), "computer name: only letters, digits, -"); fake::Server srv(objects(), BindDn, BindPw); Config admin = testConfig(srv.port()); std::string secret = dir + "/secrets/bind-example.com.secret"; auto utf16 = [](const std::string &s) { std::string q = "\"" + s + "\"", o; for (char c : q) { o += c; o += '\0'; } return o; }; try { JoinResult r = joinWith(admin, "fenrir1.lan", secret, false); CHECK(r.created && r.principal == "FENRIR1$" && r.realm == "EXAMPLE.COM" && r.secretFile == secret, "join: a new account"); fake::Object o; CHECK(srv.object("CN=FENRIR1,CN=Computers,DC=example,DC=com", o), "join: CN=FENRIR1,CN=Computers"); std::map<std::string, std::vector<std::string>> a(o.attrs.begin(), o.attrs.end()); std::ifstream in(secret); std::string pw; std::getline(in, pw); CHECK(pw.size() == 32 && a["unicodePwd"].size() == 1 && a["unicodePwd"][0] == utf16(pw), "join: the password in unicodePwd (quoted, UTF-16LE) and in the secret file"); CHECK(a["sAMAccountName"] == std::vector<std::string>{ "FENRIR1$" } && a["userAccountControl"][0] == "4096" && a["msDS-SupportedEncryptionTypes"][0] == "24" && a["dNSHostName"][0] == "fenrir1.example.com" && a["servicePrincipalName"].size() == 2, "join: the account's attributes"); struct stat st; CHECK(stat(secret.c_str(), &st) == 0 && (st.st_mode & 077) == 0, "join: the secret file root only"); JoinResult again = joinWith(admin, "fenrir1", secret, false); std::ifstream in2(secret); std::string pw2; std::getline(in2, pw2); fake::Object o2; srv.object("CN=FENRIR1,CN=Computers,DC=example,DC=com", o2); std::map<std::string, std::vector<std::string>> a2(o2.attrs.begin(), o2.attrs.end()); CHECK(!again.created && pw2 != pw && a2["unicodePwd"][0] == utf16(pw2), "join again: the existing account, a new password"); } catch (const std::exception &e) { CHECK(false, std::string("join: ") + e.what()); } Config wrong = admin; wrong.bindPassword = "nope"; CHECK(throws([&] { joinWith(wrong, "fenrir2", secret, false); }), "join: a wrong admin password fails"); } time_t fakeNow = 1000; void testCache(const std::string &dir) { Loading Loading @@ -935,6 +1007,8 @@ int main() { testSid(); testConfig(dir); testCache(dir); testDiscover(); testJoin(dir); testKrb5Crypto(); testKeytab(dir); testGss(); Loading