1 /*
2 * Copyright 2024–2026 Michael Osipov
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.sf.michaelo.tomcat.pac.asn1;
17
18 import java.math.BigInteger;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Objects;
22
23 import com.sun.security.jgss.AuthorizationDataEntry;
24
25 import org.apache.tomcat.util.buf.Asn1Parser;
26
27 /**
28 * A minimalist ASN.1 parser for Kerberos {@code AuthorizationData} according to RFC 4120, section 5.2.6 for the
29 * {@code AD-IF-RELEVANT} type. It unwraps all nested data as described in <a href=
30 * "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/21181737-74fd-492c-bfbd-0322993a9061">MS-PAC
31 * structure</a>.
32 */
33 public class AdIfRelevantAsn1Parser {
34
35 /*
36 * AD types as per: https://github.com/krb5/krb5-assignments/blob/master/ad-type
37 */
38 public static final int AD_IF_RELEVANT = 1;
39 public static final int AD_WIN2K_PAC = 128;
40
41 private AdIfRelevantAsn1Parser() {}
42
43 /**
44 * Parses the ASN.1 structure and converts to a list of {@code AuthorizationDataEntry} elements.
45 *
46 * @param adIfRelevant ASN.1 encoded data
47 * @return a list of {@code AuthorizationDataEntry} elements
48 * @throws NullPointerException if {@code adIfRelevant} is null
49 * @throws IllegalArgumentException if {@code adIfRelevant} is empty
50 */
51 public static List<AuthorizationDataEntry> parse(byte[] adIfRelevant) {
52 Objects.requireNonNull(adIfRelevant, "adIfRelevant cannot be null");
53 if (adIfRelevant.length == 0) throw new IllegalArgumentException("adIfRelevant cannot be empty");
54
55 List<AuthorizationDataEntry> adEntries = new ArrayList<>();
56
57 Asn1Parser parser = new Asn1Parser(adIfRelevant);
58 parser.parseTagSequence();
59 parser.parseFullLength();
60 while (!parser.eof()) {
61 parser.parseTagSequence();
62 parser.parseLength();
63 Asn1Parser p = new Asn1Parser(parser.parseAttributeAsBytes(0));
64 BigInteger type = p.parseInt();
65 p = new Asn1Parser(parser.parseAttributeAsBytes(1));
66 byte[] data = p.parseOctetString();
67 adEntries.add(new AuthorizationDataEntry(type.intValue(), data));
68 }
69
70 return adEntries;
71 }
72 }