View Javadoc
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  }