View Javadoc
1   /*
2    * Copyright 2024 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;
17  
18  import java.nio.charset.StandardCharsets;
19  import java.util.Objects;
20  
21  import net.sf.michaelo.tomcat.realm.Sid;
22  
23  /**
24   * A class representing the <a href=
25   * "https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/1c0d6e11-6443-4846-b744-f9f810a504eb">{@code UPN_DNS_INFO}</a>
26   * structure from MS-PAC.
27   */
28  public class UpnDnsInfo {
29  
30  	public final static long UPN_CONSTRUCTED_FLAG = 0x00000001L;
31  	public final static long SAM_NAME_AND_SID_FLAG = 0x00000002L;
32  
33  	private final String upn;
34  	private final String dnsDomainName;
35  
36  	private final long flags;
37  
38  	private String samName;
39  	private Sid sid;
40  
41  	/**
42  	 * Parses a UPN DNS info object from a byte array.
43  	 *
44  	 * @param infoBytes
45  	 *            UPN DNS info structure encoded as bytes
46  	 * @throws NullPointerException
47  	 *             if {@code infoBytes} is null
48  	 * @throws IllegalArgumentException
49  	 *             if {@code infoBytes} is empty
50  	 */
51  	public UpnDnsInfo(byte[] infoBytes) {
52  		Objects.requireNonNull(infoBytes, "infoBytes cannot be null");
53  		if (infoBytes.length == 0)
54  			throw new IllegalArgumentException("infoBytes cannot be empty");
55  
56  		PacDataBuffer buf = new PacDataBuffer(infoBytes);
57  
58  		// UpnLength
59  		int upnLength = buf.getUnsignedShort();
60  		// UpnOffset
61  		int upnOffset = buf.getUnsignedShort();
62  		// DnsDomainNameLength
63  		int dnsDomainNameLength = buf.getUnsignedShort();
64  		// DnsDomainNameOffset
65  		int dnsDomainNameOffset = buf.getUnsignedShort();
66  		// Flags
67  		/* Something isn't right, it appears to be that the bits are in reverse order
68  		 * or the documentation is wrong:
69  		 * - flag U should be at bit 31, but is at bit 0
70  		 * - flag S should be at bit 30, but is at bit 1
71  		 *
72  		 * Samba has the same reversed order: https://github.com/samba-team/samba/blob/9844ac289be3430fd3f72c5e57fa00e012c5d417/librpc/idl/krb5pac.idl#L93-L96
73  		 */
74  		this.flags = buf.getUnsignedInt();
75  
76  		int pos = buf.position();
77  		buf.position(upnOffset);
78  		this.upn = getUnicodeString(buf, upnLength);
79  		buf.position(dnsDomainNameOffset);
80  		this.dnsDomainName = getUnicodeString(buf, dnsDomainNameLength);
81  		buf.position(pos);
82  
83  		if ((flags & SAM_NAME_AND_SID_FLAG) != 0L) {
84  			// SamNameLength
85  			int samNameLength = buf.getUnsignedShort();
86  			// SamNameOffset
87  			int samNameOffset = buf.getUnsignedShort();
88  			// SidLength
89  			int sidLength = buf.getUnsignedShort();
90  			// SidOffset
91  			int sidOffset = buf.getUnsignedShort();
92  
93  			pos = buf.position();
94  			buf.position(samNameOffset);
95  			this.samName = getUnicodeString(buf, samNameLength);
96  			byte[] dst = new byte[sidLength];
97  			buf.position(sidOffset);
98  			buf.get(dst);
99  			this.sid = new Sid(dst);
100 		}
101 	}
102 
103 	public String getUpn() {
104 		return upn;
105 	}
106 
107 	public String getDnsDomainName() {
108 		return dnsDomainName;
109 	}
110 
111 	public long getFlags() {
112 		return flags;
113 	}
114 
115 	public String getSamName() {
116 		return samName;
117 	}
118 
119 	public Sid getSid() {
120 		return sid;
121 	}
122 
123 	private String getUnicodeString(PacDataBuffer buf, int length) {
124 		byte[] dst = new byte[length];
125 		buf.get(dst);
126 		return new String(dst, StandardCharsets.UTF_16LE);
127 	}
128 
129 }