Branch: master

c70ce69a 2015-09-29 02:08:05 Timothy Pearson
Convert the last methods using the kadmin utility to the Heimdal C API
M src/libtdeldap.cpp
M src/libtdeldap.h
diff --git a/src/libtdeldap.cpp b/src/libtdeldap.cpp
index 19812d5..690cee9 100644
--- a/src/libtdeldap.cpp
+++ b/src/libtdeldap.cpp
@@ -1111,17 +1111,14 @@
 	}
 }
 
-// WARNING
-// kadmin does not have a standard "waiting for user input" character or sequence
-// To make matters worse, the colon does not uniquely designate the end of a line; for example the response "kadmin: ext openldap/foo.bar.baz: Principal does not exist"
-// One way around this would be to see if the first colon is part of a "kadmin:" string; if so, then the colon is not a reliable end of line indicator for the current line
-// (in fact only '\r' should be used as the end of line indicator in that case)
+// FIXME
+// Convert anything relying on this method to the Heimdal C API
 TQString LDAPManager::readFullLineFromPtyProcess(PtyProcess* proc) {
 	TQString result = "";
 	while ((!result.contains("\r")) &&
 		(!result.contains(">")) &&
-		(!((!result.contains("kadmin:")) && (!result.contains("kinit:")) && (!result.contains("ktutil:")) && result.contains(":"))) &&
-		(!((result.contains("kadmin:")) && (!result.contains("kinit:")) && (!result.contains("ktutil:")) && result.contains("\r")))
+		(!((!result.contains("kinit:")) && (!result.contains("ktutil:")) && result.contains(":"))) &&
+		(!((!result.contains("kinit:")) && (!result.contains("ktutil:")) && result.contains("\r")))
 		) {
 		result = result + TQString(proc->readLine(false));
 		tqApp->processEvents();
@@ -1307,7 +1304,7 @@
 	}
 	if (retcode == 0) {
 		retcode = 1;
-		krb5_principal user_kadm5_principal;
+		krb5_principal user_kadm5_principal = NULL;
 		krb5adm_ret = krb5_parse_name(m_krb5admContext, user.name.ascii(), &user_kadm5_principal);
 		if (krb5adm_ret) {
 			if (errstr) *errstr = i18n("%1<p>Details:<br>Failed to execute krb5_parse_name for user '%2' (code %3)").arg(krb5_get_error_message(m_krb5admContext, krb5adm_ret)).arg(user.name).arg(krb5adm_ret);
@@ -1622,15 +1619,15 @@
 	}
 
 	TQString prompt;
-	PtyProcess kadminProc;
-	kadminProc.exec(command, args);
-	prompt = readFullLineFromPtyProcess(&kadminProc);
+	PtyProcess kinitProc;
+	kinitProc.exec(command, args);
+	prompt = readFullLineFromPtyProcess(&kinitProc);
 	prompt = prompt.stripWhiteSpace();
 	if (prompt.endsWith(" Password:")) {
-		kadminProc.enableLocalEcho(false);
-		kadminProc.writeLine(creds.password, true);
+		kinitProc.enableLocalEcho(false);
+		kinitProc.writeLine(creds.password, true);
 		do { // Discard our own input
-			prompt = readFullLineFromPtyProcess(&kadminProc);
+			prompt = readFullLineFromPtyProcess(&kinitProc);
 			printf("(kinit) '%s'\n", prompt.ascii());
 		} while (prompt == "");
 		prompt = prompt.stripWhiteSpace();
@@ -1994,6 +1991,48 @@
 
 		if (password) {
 			free(password);
+		}
+
+		if (kadmin_unbind_needed) {
+			unbindKAdmin();
+			unbind(true);	// Using kadmin can disrupt our LDAP connection
+		}
+	}
+
+	return retcode;
+}
+
+int LDAPManager::kAdminDeletePrincipal(TQString principalName, TQString *errstr) {
+	int retcode;
+	kadm5_ret_t krb5adm_ret;
+
+	bool kadmin_unbind_needed = false;
+	if (m_krb5admHandle) {
+		retcode = 0;
+	}
+	else {
+		retcode = bindKAdmin(NULL, errstr);
+		kadmin_unbind_needed = true;
+	}
+	if (retcode == 0) {
+		retcode = 1;
+		krb5_principal principal_entry = NULL;
+		krb5adm_ret = krb5_parse_name(m_krb5admContext, principalName.ascii(), &principal_entry);
+		if (krb5adm_ret) {
+			if (errstr) *errstr = i18n("%1<p>Details:<br>Failed to execute krb5_parse_name for principal '%2' (code %3)").arg(krb5_get_error_message(m_krb5admContext, krb5adm_ret)).arg(principalName).arg(krb5adm_ret);
+		}
+		else {
+			krb5adm_ret = kadm5_delete_principal(m_krb5admHandle, principal_entry);
+			if (krb5adm_ret) {
+				if (errstr) *errstr = i18n("%1<p>Details:<br>Failed to execute kadm5_delete_principal for principal '%2' (code %3)").arg(krb5_get_error_message(m_krb5admContext, krb5adm_ret)).arg(principalName).arg(krb5adm_ret);
+			}
+			else {
+				// Success!
+				retcode = 0;
+			}
+
+			// Clean up
+			krb5_free_principal(m_krb5admContext, principal_entry);
 		}
 
 		if (kadmin_unbind_needed) {
@@ -2690,6 +2729,65 @@
 						free(keys);
 					}
 					kadm5_free_principal_ent(m_krb5admHandle, &principal_record);
+				}
+			}
+			krb5_kt_close(m_krb5admContext, keytab);
+		}
+
+		if (kadmin_unbind_needed) {
+			unbindKAdmin();
+			unbind(true);	// Using kadmin can disrupt our LDAP connection
+		}
+	}
+
+	return retcode;
+}
+
+int LDAPManager::deleteKeytabEntriesForPrincipal(TQString principal, TQString fileName, TQString *errstr) {
+	int retcode;
+	kadm5_ret_t krb5adm_ret;
+
+	bool kadmin_unbind_needed = false;
+	if (m_krb5admHandle) {
+		retcode = 0;
+	}
+	else {
+		retcode = bindKAdmin(NULL, errstr);
+		kadmin_unbind_needed = true;
+	}
+	if (retcode == 0) {
+		retcode = 1;
+
+		krb5_keytab keytab;
+		if (fileName == "") {
+			krb5adm_ret = krb5_kt_default(m_krb5admContext, &keytab);
+		}
+		else {
+			krb5adm_ret = krb5_kt_resolve(m_krb5admContext, fileName.ascii(), &keytab);
+		}
+		if (krb5adm_ret) {
+			if (errstr) *errstr = i18n("%1<p>Details:<br>Failed to open keytab file '%2' (code %3)").arg(krb5_get_error_message(m_krb5admContext, krb5adm_ret)).arg(fileName).arg(krb5adm_ret);
+		}
+		else {
+			krb5_principal principal_entry = NULL;
+
+			krb5adm_ret = krb5_parse_name(m_krb5admContext, principal.ascii(), &principal_entry);
+			if (krb5adm_ret) {
+				if (errstr) *errstr = i18n("%1<p>Details:<br>Failed to execute krb5_parse_name (code %2)").arg(krb5_get_error_message(m_krb5admContext, krb5adm_ret)).arg(krb5adm_ret);
+			}
+			else {
+				krb5_keytab_entry keytab_entry;
+				keytab_entry.principal = principal_entry;
+				keytab_entry.keyblock.keytype = 0;
+				keytab_entry.vno = 0;
+
+				krb5adm_ret = krb5_kt_remove_entry(m_krb5admContext, keytab, &keytab_entry);
+				if (krb5adm_ret) {
+					if (errstr) *errstr = i18n("%1<p>Details:<br>Failed to execute krb5_kt_remove_entry (code %2)").arg(krb5_get_error_message(m_krb5admContext, krb5adm_ret)).arg(krb5adm_ret);
+				}
+				else {
+					// Success!
+					retcode = 0;
 				}
 			}
 			krb5_kt_close(m_krb5admContext, keytab);
@@ -5197,65 +5295,28 @@
 int LDAPManager::unbondRealm(LDAPRealmConfig realmcfg, TQString adminUserName, const char * adminPassword, TQString adminRealm, TQString *errstr) {
 	Q_UNUSED(realmcfg);
 
-	TQCString command = "kadmin";
-	QCStringList args;
-	args << TQCString("-p") << TQCString(adminUserName+"@"+(adminRealm.upper()));
+	LDAPCredentials admincreds;
+	admincreds.username = adminUserName;
+	admincreds.password = adminPassword;
+	admincreds.realm = adminRealm;
+	admincreds.use_gssapi = false;
 
-	TQString hoststring = "host/"+getMachineFQDN();
+	TQString hoststring = "host/" + getMachineFQDN();
 
-	TQString hostprinc = TQStringList::split(".", hoststring)[0];
-	hostprinc.append("@"+(adminRealm.upper()));
-
-	TQString prompt;
-	PtyProcess kadminProc;
-	kadminProc.exec(command, args);
-	prompt = readFullLineFromPtyProcess(&kadminProc);
-	prompt = prompt.stripWhiteSpace();
-	if (prompt == "kadmin>") {
-		command = TQCString("delete "+hoststring);
-		kadminProc.enableLocalEcho(false);
-		kadminProc.writeLine(command, true);
-		do { // Discard our own input
-			prompt = readFullLineFromPtyProcess(&kadminProc);
-			printf("(kadmin) '%s'\n", prompt.ascii());
-		} while ((prompt == TQString(command)) || (prompt == ""));
-		prompt = prompt.stripWhiteSpace();
-		if (prompt.endsWith(" Password:")) {
-			kadminProc.enableLocalEcho(false);
-			kadminProc.writeLine(adminPassword, true);
-			do { // Discard our own input
-				prompt = readFullLineFromPtyProcess(&kadminProc);
-				printf("(kadmin) '%s'\n", prompt.ascii());
-			} while (prompt == "");
-			prompt = prompt.stripWhiteSpace();
+	int retcode;
+	LDAPManager* ldap_mgr = new LDAPManager(adminRealm, TQString::null);
+	retcode = ldap_mgr->bindKAdmin(&admincreds, errstr);
+	if (!retcode) {
+		retcode = ldap_mgr->kAdminDeletePrincipal(hoststring, errstr);
+		if (!retcode) {
+			// Principal and associated keys deleted from server, now delete keys from local keytab...
+			retcode = ldap_mgr->deleteKeytabEntriesForPrincipal(hoststring, TQString::null, errstr);
 		}
-		if (prompt != "kadmin>") {
-			if (errstr) *errstr = prompt;
-			do { // Wait for command prompt
-				prompt = readFullLineFromPtyProcess(&kadminProc);
-				printf("(kadmin) '%s'\n", prompt.ascii());
-			} while (prompt == "");
-			kadminProc.enableLocalEcho(false);
-			kadminProc.writeLine("quit", true);
-			return 1;
-		}
-
-		// Success!
-		kadminProc.enableLocalEcho(false);
-		kadminProc.writeLine("quit", true);
-
-		// Delete keys from keytab
-		command = TQString("ktutil remove -p %1").arg(hoststring+"@"+adminRealm.upper());
-		if (system(command) < 0) {
-			printf("ERROR: Execution of \"%s\" failed!\n", command.data());
-			return 1;	// Failure
-		}
-
-		// Success!
-		return 0;
+		ldap_mgr->unbindKAdmin();
 	}
+	delete ldap_mgr;
 
 ** Diff limit reached (max: 250 lines) **