View Javadoc
1   /*
2    * Copyright 2013–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.realm;
17  
18  import java.nio.Buffer;
19  import java.nio.ByteBuffer;
20  import java.nio.ByteOrder;
21  import java.util.Arrays;
22  
23  /**
24   * A class representing a <a href=
25   * "https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/f992ad60-0fe4-4b87-9fed-beb478836861">{@code SID}
26   * (security identifier)</a> from MS-DTYP.
27   */
28  public class Sid {
29  
30  	public static final Sid NULL_SID = new Sid(new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00,
31  			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
32  			(byte) 0x00, (byte) 0x00, (byte) 0x00 });
33  
34  	public static final Sid ANONYMOUS_SID = new Sid(new byte[] { (byte) 0x01, (byte) 0x01,
35  			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x05,
36  			(byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00 });
37  
38  	private byte[] bytes;
39  
40  	private int revision;
41  	private int subAuthorityCount;
42  	private byte[] identifierAuthority;
43  	private long[] subAuthorities;
44  
45  	private String sidString;
46  
47  	/**
48  	 * Parses a SID object from a byte array.
49  	 *
50  	 * @param sidBytes
51  	 *            SID structure encoded as bytes
52  	 * @throws NullPointerException
53  	 *             if {@code sidBytes} is null
54  	 * @throws IllegalArgumentException
55  	 *             if {@code sidBytes} contains less than 12 bytes
56  	 * @throws IllegalArgumentException
57  	 *             if SID's revision is not 1
58  	 * @throws IllegalArgumentException
59  	 *             if SID's subauthority count is more than 15
60  	 */
61  	public Sid(byte[] sidBytes) {
62  		if (sidBytes == null)
63  			throw new NullPointerException("sidBytes cannot be null");
64  		if (sidBytes.length < 12)
65  			throw new IllegalArgumentException(
66  					"SID must be at least 12 bytes long but is " + sidBytes.length);
67  
68  		ByteBuffer buf = ByteBuffer.wrap(sidBytes);
69  		buf.order(ByteOrder.LITTLE_ENDIAN);
70  
71  		// Always 0x01
72  		this.revision = buf.get() & 0xFF;
73  		if (this.revision != 0x01)
74  			throw new IllegalArgumentException("SID revision must be 1 but is " + this.revision);
75  
76  		// At most 15 subauthorities
77  		this.subAuthorityCount = buf.get() & 0xFF;
78  		if (this.subAuthorityCount > 15)
79  			throw new IllegalArgumentException(
80  					"SID subauthority count must be at most 15 but is " + this.subAuthorityCount);
81  
82  		this.identifierAuthority = new byte[6];
83  		buf.get(this.identifierAuthority);
84  
85  		StringBuilder sidStringBuilder = new StringBuilder("S");
86  
87  		sidStringBuilder.append('-').append(this.revision);
88  
89  		ByteBuffer iaBuf = ByteBuffer.allocate(Long.SIZE / Byte.SIZE);
90  		((Buffer) iaBuf).position(2);
91  		iaBuf.put(this.identifierAuthority);
92  		((Buffer) iaBuf).flip();
93  
94  		sidStringBuilder.append('-').append(iaBuf.getLong());
95  
96  		this.subAuthorities = new long[this.subAuthorityCount];
97  		for (byte b = 0; b < this.subAuthorityCount; b++) {
98  			this.subAuthorities[b] = buf.getInt() & 0xffffffffL;
99  
100 			sidStringBuilder.append('-').append(this.subAuthorities[b]);
101 		}
102 
103 		this.bytes = Arrays.copyOf(sidBytes, sidBytes.length);
104 		this.sidString = sidStringBuilder.toString();
105 	}
106 
107 	public Sid append(long relativeId) {
108 		byte[] sidBytes = this.bytes;
109 		byte[] appendedSidBytes = new byte[sidBytes.length + 4];
110 		System.arraycopy(sidBytes, 0, appendedSidBytes, 0, sidBytes.length);
111 		appendedSidBytes[1] = (byte) (this.subAuthorityCount + 1);
112 		int signedRelativeId = (int) (relativeId);
113 		appendedSidBytes[sidBytes.length + 0] = (byte) (signedRelativeId & 0xFF);
114 		appendedSidBytes[sidBytes.length + 1] = (byte) ((signedRelativeId >> 8) & 0xFF);
115 		appendedSidBytes[sidBytes.length + 2] = (byte) ((signedRelativeId >> 16) & 0xFF);
116 		appendedSidBytes[sidBytes.length + 3] = (byte) ((signedRelativeId >> 24) & 0xFF);
117 		return new Sid(appendedSidBytes);
118 	}
119 
120 	public byte[] getBytes() {
121 		return Arrays.copyOf(bytes, bytes.length);
122 	}
123 
124 	@Override
125 	public boolean equals(Object obj) {
126 		if (obj == null)
127 			return false;
128 
129 		if (!(obj instanceof Sid))
130 			return false;
131 
132 		Sid that = (Sid) obj;
133 
134 		if (this == that)
135 			return true;
136 
137 		return Arrays.equals(this.bytes, that.bytes);
138 	}
139 
140 	@Override
141 	public int hashCode() {
142 		return Arrays.hashCode(this.bytes);
143 	}
144 
145 	@Override
146 	public String toString() {
147 		return sidString;
148 	}
149 
150 }