All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.michaelo.tomcat.pac.Krb5AuthzDataDumpPrinter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2024 Michael Osipov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.sf.michaelo.tomcat.pac;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicInteger;

import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KeyTab;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import com.sun.security.jgss.AuthorizationDataEntry;

import net.sf.michaelo.tomcat.authenticator.SpnegoAuthenticator;
import net.sf.michaelo.tomcat.pac.asn1.AdIfRelevantAsn1Parser;
import net.sf.michaelo.tomcat.realm.Krb5AuthzDataDumpingActiveDirectoryRealm;
import net.sf.michaelo.tomcat.realm.Sid;

/**
 * A Kerberos {@code AuthorizationData} dump printer produced by
 * {@link Krb5AuthzDataDumpingActiveDirectoryRealm}.
 * 

* This class can be called via its main method, it supports the following optional parameters: *

    *
  • output format {@code --format} {@code listing} (default) or {@code sql},
  • *
  • verify the PAC server signature with the {@link PrivateSunPacSignatureVerifier} and a login * context {@code --verify-signature} {loginEntryName}, The configuration of the login * entry must be identical to the one from {@link SpnegoAuthenticator#getLoginEntryName()}
  • *
* and the following positional parameters: *
    *
  • dump file/directory {@code path...}: either a file or a directory containing dumps.
  • *
*

* The {@code sql} format output can be used to import the data into a SQLite database for later * analysis. */ public class Krb5AuthzDataDumpPrinter { private static final AtomicInteger PAC_ID_GENERATOR = new AtomicInteger(); private static void dumpFile(Path file, String format, KerberosKey[] keys) throws IOException, SignatureException { System.err.printf("Processing file '%s'%n", file); List adEntries = new ArrayList<>(); try (Scanner scanner = new Scanner(file)) { while (scanner.hasNext()) { int type = scanner.nextInt(); byte[] data = Base64.getDecoder().decode(scanner.next()); adEntries.add(new AuthorizationDataEntry(type, data)); } } for (AuthorizationDataEntry adEntry : adEntries) { int type = adEntry.getType(); byte[] data = adEntry.getData(); switch (type) { case AdIfRelevantAsn1Parser.AD_IF_RELEVANT: List adIfRelevantEntries = AdIfRelevantAsn1Parser .parse(data); for (AuthorizationDataEntry adIfRelevantEntry : adIfRelevantEntries) { int adIfRelevantType = adIfRelevantEntry.getType(); byte[] adIfRelevantTypeData = adIfRelevantEntry.getData(); switch (adIfRelevantType) { case AdIfRelevantAsn1Parser.AD_WIN2K_PAC: int pacId = PAC_ID_GENERATOR.incrementAndGet(); Pac pac = new Pac(adIfRelevantTypeData, new PrivateSunPacSignatureVerifier()); if (keys != null) { if (format.equals("listing")) System.out.print("Verifying PAC server signature..."); pac.verifySignature(keys); if (format.equals("listing")) System.out.println("PASSED"); } KerbValidationInfo kerbValidationInfo = pac.getKerbValidationInfo(); String effectiveName = kerbValidationInfo.getEffectiveName(); String fullName = kerbValidationInfo.getFullName(); String logonScript = kerbValidationInfo.getLogonScript(); String profilePath = kerbValidationInfo.getProfilePath(); String homeDirectory = kerbValidationInfo.getHomeDirectory(); String homeDirectoryDrive = kerbValidationInfo.getHomeDirectoryDrive(); long userId = kerbValidationInfo.getUserId(); long primaryGroupId = kerbValidationInfo.getPrimaryGroupId(); List groupIds = kerbValidationInfo.getGroupIds(); long userFlags = kerbValidationInfo.getUserFlags(); String logonServer = kerbValidationInfo.getLogonServer(); String logonDomainName = kerbValidationInfo.getLogonDomainName(); Sid logonDomainId = kerbValidationInfo.getLogonDomainId(); long userAccountControl = kerbValidationInfo.getUserAccountControl(); List extraSids = kerbValidationInfo.getExtraSids(); Sid resourceGroupDomainSid = kerbValidationInfo.getResourceGroupDomainSid(); List resourceGroupIds = kerbValidationInfo .getResourceGroupIds(); switch (format) { case "listing": System.out.println("KerbValidationInfo:"); System.out.println(" effectiveName: " + effectiveName); System.out.println(" fullName: " + fullName); System.out.println(" logonScript: " + logonScript); System.out.println(" profilePath: " + profilePath); System.out.println(" homeDirectory: " + homeDirectory); System.out.println(" homeDirectoryDrive: " + homeDirectoryDrive); System.out.println(" userId: " + userId); System.out.println(" primaryGroupId: " + primaryGroupId); System.out.println(" groupIds (" + groupIds.size() + "):"); for (GroupMembership groupId : groupIds) { System.out.println(" - " + groupId + " (" + logonDomainId.append(groupId.getRelativeId()) + ")"); } System.out.printf(" userFlags: 0x%08X%n", userFlags); System.out.println(" logonServer: " + logonServer); System.out.println(" logonDomainName: " + logonDomainName); System.out.println(" logonDomainId: " + logonDomainId); System.out.printf(" userAccountControl: 0x%08X%n", userAccountControl); if (extraSids != null) { System.out.println(" extraSids (" + extraSids.size() + "):"); for (KerbSidAndAttributes extraSid : extraSids) { System.out.println(" - " + extraSid); } } if (resourceGroupDomainSid != null) { System.out.println( " resourceGroupDomainSid: " + resourceGroupDomainSid); System.out.println( " resourceGroupIds (" + resourceGroupIds.size() + "):"); for (GroupMembership resourceGroupId : resourceGroupIds) { System.out.println(" - " + resourceGroupId + " (" + resourceGroupDomainSid.append(resourceGroupId.getRelativeId()) + ")"); } } break; case "sql": System.out.printf( "insert into kerb_validation_info(pacId, effectiveName, fullName, logonScript, profilePath, homeDirectory, homeDirectoryDrive, userId, primaryGroupId, userFlags, logonServer, logonDomainName, logonDomainId, userAccountControl, resourceGroupDomainSid)" + " values(%d, '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', %d, %s);%n", pacId, effectiveName, fullName, logonScript, profilePath, homeDirectory, homeDirectoryDrive, userId, primaryGroupId, userFlags, logonServer, logonDomainName, logonDomainId, userAccountControl, nullSafe(resourceGroupDomainSid)); for (GroupMembership groupId : groupIds) { System.out.printf( "insert into group_ids(pacId, relativeId, attributes) values(%d, %d, %d);%n", pacId, groupId.getRelativeId(), groupId.getAttributes()); } if (extraSids != null) { for (KerbSidAndAttributes extraSid : extraSids) { System.out.printf( "insert into extra_sids(pacId, sid, attributes) values(%d, '%s', %d);%n", pacId, extraSid.getSid(), extraSid.getAttributes()); } } if (resourceGroupDomainSid != null) { for (GroupMembership resourceGroupId : resourceGroupIds) { System.out.printf( "insert into resource_group_ids(pacId, relativeId, attributes) values(%d, %d, %d);%n", pacId, resourceGroupId.getRelativeId(), resourceGroupId.getAttributes()); } } break; } UpnDnsInfo upnDnsInfo = pac.getUpnDnsInfo(); if (upnDnsInfo != null) { String upn = upnDnsInfo.getUpn(); String dnsDomainName = upnDnsInfo.getDnsDomainName(); long flags = upnDnsInfo.getFlags(); String samName = upnDnsInfo.getSamName(); Sid sid = upnDnsInfo.getSid(); switch (format) { case "listing": System.out.println("UpnDnsInfo:"); System.out.println(" upn: " + upn); System.out.println(" dnsDomainName: " + dnsDomainName); System.out.printf(" flags: 0x%08X%n", flags); if (samName != null) { System.out.println(" samName: " + samName); System.out.println(" sid: " + sid); } break; case "sql": System.out.printf( "insert into upn_dns_info(pacId, upn, dnsDomainName, flags, samName, sid)" + " values(%d, '%s', '%s', %d, %s, %s);%n", pacId, upn, dnsDomainName, flags, nullSafe(samName), nullSafe(sid)); break; } } PacClientInfo pacClientInfo = pac.getPacClientInfo(); switch (format) { case "listing": System.out.println("PacClientInfo:"); System.out.println(" name: " + pacClientInfo.getName()); break; } PacSignatureData serverSignature = pac.getServerSignature(); PacSignatureData kdcSignature = pac.getKdcSignature(); switch (format) { case "listing": System.out.println("ServerSignature:"); System.out.println(" type: " + serverSignature.getType()); System.out.println(" signature: " + Base64.getEncoder() .encodeToString(serverSignature.getSignature())); System.out.println("KdcSignature:"); System.out.println(" type: " + kdcSignature.getType()); System.out.println(" signature: " + Base64.getEncoder() .encodeToString(kdcSignature.getSignature())); break; } break; default: System.err.println( "Ignoring unsupported authorization data (AD-IF-RELEVANT) entry type " + adIfRelevantType + " with data " + Base64.getEncoder().encodeToString(adIfRelevantTypeData)); break; } } break; default: System.err.println("Ignoring unsupported authorization data entry type " + type + " with data " + Base64.getEncoder().encodeToString(data)); break; } } } private static String nullSafe(Object obj) { return obj != null ? "'" + obj + "'" : "NULL"; } public static void main(String[] args) throws IOException, SignatureException { if (args.length == 0) { System.err.println("No arguments provided"); System.exit(1); } int positionalArgs = 0; String formatValue = "listing"; String verifySignatureValue = null; boolean breakLoop = false; while (positionalArgs < args.length && !breakLoop) { switch (args[positionalArgs]) { case "--format": positionalArgs++; if (positionalArgs > args.length - 1) throw new IllegalArgumentException("Missing option value for '--format'"); formatValue = args[positionalArgs++]; break; case "--verify-signature": positionalArgs++; if (positionalArgs > args.length - 1) throw new IllegalArgumentException( "Missing option value for '--verify-signature'"); verifySignatureValue = args[positionalArgs++]; break; case "--": positionalArgs++; breakLoop = true; break; default: breakLoop = true; break; } } if (!formatValue.equals("listing") && !formatValue.equals("sql")) throw new IllegalArgumentException("Unsupported format value: " + formatValue); KerberosKey[] keysValue = null; if (verifySignatureValue != null) { String loginEntryName = verifySignatureValue; LoginContext lc = null; try { lc = new LoginContext(loginEntryName); lc.login(); Subject subject = lc.getSubject(); KerberosPrincipal principal = subject.getPrincipals(KerberosPrincipal.class) .iterator().next(); KeyTab keyTab = subject.getPrivateCredentials(KeyTab.class).iterator().next(); keysValue = keyTab.getKeys(principal); } catch (LoginException e) { throw new IllegalStateException( "Failed to load Kerberos keys for login entry '" + loginEntryName + "'", e); } finally { if (lc != null) { try { lc.logout(); } catch (LoginException e) { ; // Ignore } } } } final KerberosKey[] keys = keysValue; final String format = formatValue; if (format.equals("sql")) { System.out.println("BEGIN TRANSACTION;"); try (BufferedReader r = new BufferedReader(new InputStreamReader( Krb5AuthzDataDumpPrinter.class .getResourceAsStream("/net/sf/michaelo/tomcat/pac/create-tables.sql"), StandardCharsets.UTF_8))) { r.lines().forEach(line -> System.out.println(line)); } } for (int i = positionalArgs; i < args.length; i++) { Path path = Paths.get(args[i]); if (Files.notExists(path)) { System.err.printf("Ignoring non-existing path '%s'%n", path); continue; } if (Files.isRegularFile(path)) { dumpFile(path, format, keys); } else if (Files.isDirectory(path)) { Files.walk(path).filter(Files::isRegularFile).forEach(file -> { try { dumpFile(file, format, keys); } catch (IOException e) { throw new UncheckedIOException(e); } catch (SignatureException e) { throw new RuntimeException(e); } }); } else { System.err.printf("Ignoring unsupported path '%s'%n", path); continue; } } if (format.equals("sql")) { System.out.println("COMMIT;"); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy