1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.ssl;
17
18 import io.netty.internal.tcnative.SSL;
19 import io.netty.internal.tcnative.SSLContext;
20 import io.netty.internal.tcnative.SessionTicketKey;
21 import io.netty5.util.internal.ObjectUtil;
22
23 import javax.net.ssl.SSLSession;
24 import javax.net.ssl.SSLSessionContext;
25 import java.util.Arrays;
26 import java.util.Enumeration;
27 import java.util.Iterator;
28 import java.util.concurrent.locks.Lock;
29
30 import static java.util.Objects.requireNonNull;
31
32
33
34
35 public abstract class OpenSslSessionContext implements SSLSessionContext {
36
37 private final OpenSslSessionStats stats;
38
39
40
41
42 private final OpenSslKeyMaterialProvider provider;
43
44 final ReferenceCountedOpenSslContext context;
45
46 private final OpenSslSessionCache sessionCache;
47 private final long mask;
48
49
50
51
52
53 OpenSslSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider, long mask,
54 OpenSslSessionCache cache) {
55 this.context = context;
56 this.provider = provider;
57 this.mask = mask;
58 stats = new OpenSslSessionStats(context);
59 sessionCache = cache;
60 SSLContext.setSSLSessionCache(context.ctx, cache);
61 }
62
63 final boolean useKeyManager() {
64 return provider != null;
65 }
66
67 @Override
68 public void setSessionCacheSize(int size) {
69 ObjectUtil.checkPositiveOrZero(size, "size");
70 sessionCache.setSessionCacheSize(size);
71 }
72
73 @Override
74 public int getSessionCacheSize() {
75 return sessionCache.getSessionCacheSize();
76 }
77
78 @Override
79 public void setSessionTimeout(int seconds) {
80 ObjectUtil.checkPositiveOrZero(seconds, "seconds");
81
82 Lock writerLock = context.ctxLock.writeLock();
83 writerLock.lock();
84 try {
85 SSLContext.setSessionCacheTimeout(context.ctx, seconds);
86 sessionCache.setSessionTimeout(seconds);
87 } finally {
88 writerLock.unlock();
89 }
90 }
91
92 @Override
93 public int getSessionTimeout() {
94 return sessionCache.getSessionTimeout();
95 }
96
97 @Override
98 public SSLSession getSession(byte[] bytes) {
99 return sessionCache.getSession(new OpenSslSessionId(bytes));
100 }
101
102 @Override
103 public Enumeration<byte[]> getIds() {
104 return new Enumeration<>() {
105 private final Iterator<OpenSslSessionId> ids = sessionCache.getIds().iterator();
106
107 @Override
108 public boolean hasMoreElements() {
109 return ids.hasNext();
110 }
111
112 @Override
113 public byte[] nextElement() {
114 return ids.next().cloneBytes();
115 }
116 };
117 }
118
119
120
121
122
123 @Deprecated
124 public void setTicketKeys(byte[] keys) {
125 if (keys.length % SessionTicketKey.TICKET_KEY_SIZE != 0) {
126 throw new IllegalArgumentException("keys.length % " + SessionTicketKey.TICKET_KEY_SIZE + " != 0");
127 }
128 SessionTicketKey[] tickets = new SessionTicketKey[keys.length / SessionTicketKey.TICKET_KEY_SIZE];
129 for (int i = 0, a = 0; i < tickets.length; i++) {
130 byte[] name = Arrays.copyOfRange(keys, a, SessionTicketKey.NAME_SIZE);
131 a += SessionTicketKey.NAME_SIZE;
132 byte[] hmacKey = Arrays.copyOfRange(keys, a, SessionTicketKey.HMAC_KEY_SIZE);
133 i += SessionTicketKey.HMAC_KEY_SIZE;
134 byte[] aesKey = Arrays.copyOfRange(keys, a, SessionTicketKey.AES_KEY_SIZE);
135 a += SessionTicketKey.AES_KEY_SIZE;
136 tickets[i] = new SessionTicketKey(name, hmacKey, aesKey);
137 }
138 Lock writerLock = context.ctxLock.writeLock();
139 writerLock.lock();
140 try {
141 SSLContext.clearOptions(context.ctx, SSL.SSL_OP_NO_TICKET);
142 SSLContext.setSessionTicketKeys(context.ctx, tickets);
143 } finally {
144 writerLock.unlock();
145 }
146 }
147
148
149
150
151
152
153
154 public void setTicketKeys(OpenSslSessionTicketKey... keys) {
155 requireNonNull(keys, "keys");
156 SessionTicketKey[] ticketKeys = new SessionTicketKey[keys.length];
157 for (int i = 0; i < ticketKeys.length; i++) {
158 ticketKeys[i] = keys[i].key;
159 }
160 Lock writerLock = context.ctxLock.writeLock();
161 writerLock.lock();
162 try {
163 SSLContext.clearOptions(context.ctx, SSL.SSL_OP_NO_TICKET);
164 if (ticketKeys.length > 0) {
165 SSLContext.setSessionTicketKeys(context.ctx, ticketKeys);
166 }
167 } finally {
168 writerLock.unlock();
169 }
170 }
171
172
173
174
175 public void setSessionCacheEnabled(boolean enabled) {
176 long mode = enabled ? mask | SSL.SSL_SESS_CACHE_NO_INTERNAL_LOOKUP |
177 SSL.SSL_SESS_CACHE_NO_INTERNAL_STORE : SSL.SSL_SESS_CACHE_OFF;
178 Lock writerLock = context.ctxLock.writeLock();
179 writerLock.lock();
180 try {
181 SSLContext.setSessionCacheMode(context.ctx, mode);
182 if (!enabled) {
183 sessionCache.clear();
184 }
185 } finally {
186 writerLock.unlock();
187 }
188 }
189
190
191
192
193 public boolean isSessionCacheEnabled() {
194 Lock readerLock = context.ctxLock.readLock();
195 readerLock.lock();
196 try {
197 return (SSLContext.getSessionCacheMode(context.ctx) & mask) != 0;
198 } finally {
199 readerLock.unlock();
200 }
201 }
202
203
204
205
206 public OpenSslSessionStats stats() {
207 return stats;
208 }
209
210
211
212
213 final void removeFromCache(OpenSslSessionId id) {
214 sessionCache.removeSessionWithId(id);
215 }
216
217 final boolean isInCache(OpenSslSessionId id) {
218 return sessionCache.containsSessionWithId(id);
219 }
220
221 void setSessionFromCache(String host, int port, long ssl) {
222 sessionCache.setSession(ssl, host, port);
223 }
224
225 final void destroy() {
226 if (provider != null) {
227 provider.destroy();
228 }
229 sessionCache.clear();
230 }
231 }