VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/NetworkPkg/TlsDxe/TlsProtocol.c@ 77662

Last change on this file since 77662 was 77662, checked in by vboxsync, 6 years ago

EFI: First step in UDK2018 merge. Does not build yet.

  • Property svn:eol-style set to native
File size: 21.1 KB
Line 
1/** @file
2 Implementation of EFI TLS Protocol Interfaces.
3
4 Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
5
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php.
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14**/
15
16#include "TlsImpl.h"
17
18EFI_TLS_PROTOCOL mTlsProtocol = {
19 TlsSetSessionData,
20 TlsGetSessionData,
21 TlsBuildResponsePacket,
22 TlsProcessPacket
23};
24
25/**
26 Set TLS session data.
27
28 The SetSessionData() function set data for a new TLS session. All session data should
29 be set before BuildResponsePacket() invoked.
30
31 @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
32 @param[in] DataType TLS session data type.
33 @param[in] Data Pointer to session data.
34 @param[in] DataSize Total size of session data.
35
36 @retval EFI_SUCCESS The TLS session data is set successfully.
37 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
38 This is NULL.
39 Data is NULL.
40 DataSize is 0.
41 @retval EFI_UNSUPPORTED The DataType is unsupported.
42 @retval EFI_ACCESS_DENIED If the DataType is one of below:
43 EfiTlsClientRandom
44 EfiTlsServerRandom
45 EfiTlsKeyMaterial
46 @retval EFI_NOT_READY Current TLS session state is NOT
47 EfiTlsSessionStateNotStarted.
48 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
49**/
50EFI_STATUS
51EFIAPI
52TlsSetSessionData (
53 IN EFI_TLS_PROTOCOL *This,
54 IN EFI_TLS_SESSION_DATA_TYPE DataType,
55 IN VOID *Data,
56 IN UINTN DataSize
57 )
58{
59 EFI_STATUS Status;
60 TLS_INSTANCE *Instance;
61 UINT16 *CipherId;
62 UINTN Index;
63
64 EFI_TPL OldTpl;
65
66 Status = EFI_SUCCESS;
67 CipherId = NULL;
68
69 if (This == NULL || Data == NULL || DataSize == 0) {
70 return EFI_INVALID_PARAMETER;
71 }
72
73 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
74
75 Instance = TLS_INSTANCE_FROM_PROTOCOL (This);
76
77 if (DataType != EfiTlsSessionState && Instance->TlsSessionState != EfiTlsSessionNotStarted){
78 Status = EFI_NOT_READY;
79 goto ON_EXIT;
80 }
81
82 switch (DataType) {
83 //
84 // Session Configuration
85 //
86 case EfiTlsVersion:
87 if (DataSize != sizeof (EFI_TLS_VERSION)) {
88 Status = EFI_INVALID_PARAMETER;
89 goto ON_EXIT;
90 }
91
92 Status = TlsSetVersion (Instance->TlsConn, ((EFI_TLS_VERSION *) Data)->Major, ((EFI_TLS_VERSION *) Data)->Minor);
93 break;
94 case EfiTlsConnectionEnd:
95 if (DataSize != sizeof (EFI_TLS_CONNECTION_END)) {
96 Status = EFI_INVALID_PARAMETER;
97 goto ON_EXIT;
98 }
99
100 Status = TlsSetConnectionEnd (Instance->TlsConn, *((EFI_TLS_CONNECTION_END *) Data));
101 break;
102 case EfiTlsCipherList:
103 CipherId = AllocatePool (DataSize);
104 if (CipherId == NULL) {
105 Status = EFI_OUT_OF_RESOURCES;
106 goto ON_EXIT;
107 }
108
109 for (Index = 0; Index < DataSize / sizeof (EFI_TLS_CIPHER); Index++) {
110 *(CipherId +Index) = HTONS (*(((UINT16 *) Data) + Index));
111 }
112
113 Status = TlsSetCipherList (Instance->TlsConn, CipherId, DataSize / sizeof (EFI_TLS_CIPHER));
114
115 FreePool (CipherId);
116 break;
117 case EfiTlsCompressionMethod:
118 //
119 // TLS seems only define one CompressionMethod.null, which specifies that data exchanged via the
120 // record protocol will not be compressed.
121 // More information from OpenSSL: http://www.openssl.org/docs/manmaster/ssl/SSL_COMP_add_compression_method.html
122 // The TLS RFC does however not specify compression methods or their corresponding identifiers,
123 // so there is currently no compatible way to integrate compression with unknown peers.
124 // It is therefore currently not recommended to integrate compression into applications.
125 // Applications for non-public use may agree on certain compression methods.
126 // Using different compression methods with the same identifier will lead to connection failure.
127 //
128 for (Index = 0; Index < DataSize / sizeof (EFI_TLS_COMPRESSION); Index++) {
129 Status = TlsSetCompressionMethod (*((UINT8 *) Data + Index));
130 if (EFI_ERROR (Status)) {
131 break;
132 }
133 }
134
135 break;
136 case EfiTlsExtensionData:
137 Status = EFI_UNSUPPORTED;
138 goto ON_EXIT;
139 case EfiTlsVerifyMethod:
140 if (DataSize != sizeof (EFI_TLS_VERIFY)) {
141 Status = EFI_INVALID_PARAMETER;
142 goto ON_EXIT;
143 }
144
145 TlsSetVerify (Instance->TlsConn, *((UINT32 *) Data));
146 break;
147 case EfiTlsSessionID:
148 if (DataSize != sizeof (EFI_TLS_SESSION_ID)) {
149 Status = EFI_INVALID_PARAMETER;
150 goto ON_EXIT;
151 }
152
153 Status = TlsSetSessionId (
154 Instance->TlsConn,
155 ((EFI_TLS_SESSION_ID *) Data)->Data,
156 ((EFI_TLS_SESSION_ID *) Data)->Length
157 );
158 break;
159 case EfiTlsSessionState:
160 if (DataSize != sizeof (EFI_TLS_SESSION_STATE)) {
161 Status = EFI_INVALID_PARAMETER;
162 goto ON_EXIT;
163 }
164
165 Instance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) Data;
166 break;
167 //
168 // Session information
169 //
170 case EfiTlsClientRandom:
171 Status = EFI_ACCESS_DENIED;
172 break;
173 case EfiTlsServerRandom:
174 Status = EFI_ACCESS_DENIED;
175 break;
176 case EfiTlsKeyMaterial:
177 Status = EFI_ACCESS_DENIED;
178 break;
179 //
180 // Unsupported type.
181 //
182 default:
183 Status = EFI_UNSUPPORTED;
184 }
185
186ON_EXIT:
187 gBS->RestoreTPL (OldTpl);
188 return Status;
189}
190
191/**
192 Get TLS session data.
193
194 The GetSessionData() function return the TLS session information.
195
196 @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
197 @param[in] DataType TLS session data type.
198 @param[in, out] Data Pointer to session data.
199 @param[in, out] DataSize Total size of session data. On input, it means
200 the size of Data buffer. On output, it means the size
201 of copied Data buffer if EFI_SUCCESS, and means the
202 size of desired Data buffer if EFI_BUFFER_TOO_SMALL.
203
204 @retval EFI_SUCCESS The TLS session data is got successfully.
205 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
206 This is NULL.
207 DataSize is NULL.
208 Data is NULL if *DataSize is not zero.
209 @retval EFI_UNSUPPORTED The DataType is unsupported.
210 @retval EFI_NOT_FOUND The TLS session data is not found.
211 @retval EFI_NOT_READY The DataType is not ready in current session state.
212 @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the data.
213**/
214EFI_STATUS
215EFIAPI
216TlsGetSessionData (
217 IN EFI_TLS_PROTOCOL *This,
218 IN EFI_TLS_SESSION_DATA_TYPE DataType,
219 IN OUT VOID *Data, OPTIONAL
220 IN OUT UINTN *DataSize
221 )
222{
223 EFI_STATUS Status;
224 TLS_INSTANCE *Instance;
225
226 EFI_TPL OldTpl;
227
228 Status = EFI_SUCCESS;
229
230 if (This == NULL || DataSize == NULL || (Data == NULL && *DataSize != 0)) {
231 return EFI_INVALID_PARAMETER;
232 }
233
234 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
235
236 Instance = TLS_INSTANCE_FROM_PROTOCOL (This);
237
238 if (Instance->TlsSessionState == EfiTlsSessionNotStarted &&
239 (DataType == EfiTlsSessionID || DataType == EfiTlsClientRandom ||
240 DataType == EfiTlsServerRandom || DataType == EfiTlsKeyMaterial)) {
241 Status = EFI_NOT_READY;
242 goto ON_EXIT;
243 }
244
245 switch (DataType) {
246 case EfiTlsVersion:
247 if (*DataSize < sizeof (EFI_TLS_VERSION)) {
248 *DataSize = sizeof (EFI_TLS_VERSION);
249 Status = EFI_BUFFER_TOO_SMALL;
250 goto ON_EXIT;
251 }
252 *DataSize = sizeof (EFI_TLS_VERSION);
253 *((UINT16 *) Data) = HTONS (TlsGetVersion (Instance->TlsConn));
254 break;
255 case EfiTlsConnectionEnd:
256 if (*DataSize < sizeof (EFI_TLS_CONNECTION_END)) {
257 *DataSize = sizeof (EFI_TLS_CONNECTION_END);
258 Status = EFI_BUFFER_TOO_SMALL;
259 goto ON_EXIT;
260 }
261 *DataSize = sizeof (EFI_TLS_CONNECTION_END);
262 *((UINT8 *) Data) = TlsGetConnectionEnd (Instance->TlsConn);
263 break;
264 case EfiTlsCipherList:
265 //
266 // Get the current session cipher suite.
267 //
268 if (*DataSize < sizeof (EFI_TLS_CIPHER)) {
269 *DataSize = sizeof (EFI_TLS_CIPHER);
270 Status = EFI_BUFFER_TOO_SMALL;
271 goto ON_EXIT;
272 }
273 *DataSize = sizeof(EFI_TLS_CIPHER);
274 Status = TlsGetCurrentCipher (Instance->TlsConn, (UINT16 *) Data);
275 *((UINT16 *) Data) = HTONS (*((UINT16 *) Data));
276 break;
277 case EfiTlsCompressionMethod:
278 //
279 // Get the current session compression method.
280 //
281 if (*DataSize < sizeof (EFI_TLS_COMPRESSION)) {
282 *DataSize = sizeof (EFI_TLS_COMPRESSION);
283 Status = EFI_BUFFER_TOO_SMALL;
284 goto ON_EXIT;
285 }
286 *DataSize = sizeof (EFI_TLS_COMPRESSION);
287 Status = TlsGetCurrentCompressionId (Instance->TlsConn, (UINT8 *) Data);
288 break;
289 case EfiTlsExtensionData:
290 Status = EFI_UNSUPPORTED;
291 goto ON_EXIT;
292 case EfiTlsVerifyMethod:
293 if (*DataSize < sizeof (EFI_TLS_VERIFY)) {
294 *DataSize = sizeof (EFI_TLS_VERIFY);
295 Status = EFI_BUFFER_TOO_SMALL;
296 goto ON_EXIT;
297 }
298 *DataSize = sizeof (EFI_TLS_VERIFY);
299 *((UINT32 *) Data) = TlsGetVerify (Instance->TlsConn);
300 break;
301 case EfiTlsSessionID:
302 if (*DataSize < sizeof (EFI_TLS_SESSION_ID)) {
303 *DataSize = sizeof (EFI_TLS_SESSION_ID);
304 Status = EFI_BUFFER_TOO_SMALL;
305 goto ON_EXIT;
306 }
307 *DataSize = sizeof (EFI_TLS_SESSION_ID);
308 Status = TlsGetSessionId (
309 Instance->TlsConn,
310 ((EFI_TLS_SESSION_ID *) Data)->Data,
311 &(((EFI_TLS_SESSION_ID *) Data)->Length)
312 );
313 break;
314 case EfiTlsSessionState:
315 if (*DataSize < sizeof (EFI_TLS_SESSION_STATE)) {
316 *DataSize = sizeof (EFI_TLS_SESSION_STATE);
317 Status = EFI_BUFFER_TOO_SMALL;
318 goto ON_EXIT;
319 }
320 *DataSize = sizeof (EFI_TLS_SESSION_STATE);
321 CopyMem (Data, &Instance->TlsSessionState, *DataSize);
322 break;
323 case EfiTlsClientRandom:
324 if (*DataSize < sizeof (EFI_TLS_RANDOM)) {
325 *DataSize = sizeof (EFI_TLS_RANDOM);
326 Status = EFI_BUFFER_TOO_SMALL;
327 goto ON_EXIT;
328 }
329 *DataSize = sizeof (EFI_TLS_RANDOM);
330 TlsGetClientRandom (Instance->TlsConn, (UINT8 *) Data);
331 break;
332 case EfiTlsServerRandom:
333 if (*DataSize < sizeof (EFI_TLS_RANDOM)) {
334 *DataSize = sizeof (EFI_TLS_RANDOM);
335 Status = EFI_BUFFER_TOO_SMALL;
336 goto ON_EXIT;
337 }
338 *DataSize = sizeof (EFI_TLS_RANDOM);
339 TlsGetServerRandom (Instance->TlsConn, (UINT8 *) Data);
340 break;
341 case EfiTlsKeyMaterial:
342 if (*DataSize < sizeof (EFI_TLS_MASTER_SECRET)) {
343 *DataSize = sizeof (EFI_TLS_MASTER_SECRET);
344 Status = EFI_BUFFER_TOO_SMALL;
345 goto ON_EXIT;
346 }
347 *DataSize = sizeof (EFI_TLS_MASTER_SECRET);
348 Status = TlsGetKeyMaterial (Instance->TlsConn, (UINT8 *) Data);
349 break;
350 //
351 // Unsupported type.
352 //
353 default:
354 Status = EFI_UNSUPPORTED;
355 }
356
357ON_EXIT:
358 gBS->RestoreTPL (OldTpl);
359 return Status;
360}
361
362/**
363 Build response packet according to TLS state machine. This function is only valid for
364 alert, handshake and change_cipher_spec content type.
365
366 The BuildResponsePacket() function builds TLS response packet in response to the TLS
367 request packet specified by RequestBuffer and RequestSize. If RequestBuffer is NULL and
368 RequestSize is 0, and TLS session status is EfiTlsSessionNotStarted, the TLS session
369 will be initiated and the response packet needs to be ClientHello. If RequestBuffer is
370 NULL and RequestSize is 0, and TLS session status is EfiTlsSessionClosing, the TLS
371 session will be closed and response packet needs to be CloseNotify. If RequestBuffer is
372 NULL and RequestSize is 0, and TLS session status is EfiTlsSessionError, the TLS
373 session has errors and the response packet needs to be Alert message based on error
374 type.
375
376 @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
377 @param[in] RequestBuffer Pointer to the most recently received TLS packet. NULL
378 means TLS need initiate the TLS session and response
379 packet need to be ClientHello.
380 @param[in] RequestSize Packet size in bytes for the most recently received TLS
381 packet. 0 is only valid when RequestBuffer is NULL.
382 @param[out] Buffer Pointer to the buffer to hold the built packet.
383 @param[in, out] BufferSize Pointer to the buffer size in bytes. On input, it is
384 the buffer size provided by the caller. On output, it
385 is the buffer size in fact needed to contain the
386 packet.
387
388 @retval EFI_SUCCESS The required TLS packet is built successfully.
389 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
390 This is NULL.
391 RequestBuffer is NULL but RequestSize is NOT 0.
392 RequestSize is 0 but RequestBuffer is NOT NULL.
393 BufferSize is NULL.
394 Buffer is NULL if *BufferSize is not zero.
395 @retval EFI_BUFFER_TOO_SMALL BufferSize is too small to hold the response packet.
396 @retval EFI_NOT_READY Current TLS session state is NOT ready to build
397 ResponsePacket.
398 @retval EFI_ABORTED Something wrong build response packet.
399**/
400EFI_STATUS
401EFIAPI
402TlsBuildResponsePacket (
403 IN EFI_TLS_PROTOCOL *This,
404 IN UINT8 *RequestBuffer, OPTIONAL
405 IN UINTN RequestSize, OPTIONAL
406 OUT UINT8 *Buffer, OPTIONAL
407 IN OUT UINTN *BufferSize
408 )
409{
410 EFI_STATUS Status;
411 TLS_INSTANCE *Instance;
412 EFI_TPL OldTpl;
413
414 Status = EFI_SUCCESS;
415
416 if ((This == NULL) || (BufferSize == NULL) ||
417 (RequestBuffer == NULL && RequestSize != 0) ||
418 (RequestBuffer != NULL && RequestSize == 0) ||
419 (Buffer == NULL && *BufferSize !=0)) {
420 return EFI_INVALID_PARAMETER;
421 }
422
423 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
424
425 Instance = TLS_INSTANCE_FROM_PROTOCOL (This);
426
427 if(RequestBuffer == NULL && RequestSize == 0) {
428 switch (Instance->TlsSessionState) {
429 case EfiTlsSessionNotStarted:
430 //
431 // ClientHello.
432 //
433 Status = TlsDoHandshake (
434 Instance->TlsConn,
435 NULL,
436 0,
437 Buffer,
438 BufferSize
439 );
440 if (EFI_ERROR (Status)) {
441 goto ON_EXIT;
442 }
443
444 //
445 // *BufferSize should not be zero when ClientHello.
446 //
447 if (*BufferSize == 0) {
448 Status = EFI_ABORTED;
449 goto ON_EXIT;
450 }
451
452 Instance->TlsSessionState = EfiTlsSessionHandShaking;
453
454 break;
455 case EfiTlsSessionClosing:
456 //
457 // TLS session will be closed and response packet needs to be CloseNotify.
458 //
459 Status = TlsCloseNotify (
460 Instance->TlsConn,
461 Buffer,
462 BufferSize
463 );
464 if (EFI_ERROR (Status)) {
465 goto ON_EXIT;
466 }
467
468 //
469 // *BufferSize should not be zero when build CloseNotify message.
470 //
471 if (*BufferSize == 0) {
472 Status = EFI_ABORTED;
473 goto ON_EXIT;
474 }
475
476 break;
477 case EfiTlsSessionError:
478 //
479 // TLS session has errors and the response packet needs to be Alert
480 // message based on error type.
481 //
482 Status = TlsHandleAlert (
483 Instance->TlsConn,
484 NULL,
485 0,
486 Buffer,
487 BufferSize
488 );
489 if (EFI_ERROR (Status)) {
490 goto ON_EXIT;
491 }
492
493 break;
494 default:
495 //
496 // Current TLS session state is NOT ready to build ResponsePacket.
497 //
498 Status = EFI_NOT_READY;
499 }
500 } else {
501 //
502 // 1. Received packet may have multiple TLS record messages.
503 // 2. One TLS record message may have multiple handshake protocol.
504 // 3. Some errors may be happened in handshake.
505 // TlsDoHandshake() can handle all of those cases.
506 //
507 if (TlsInHandshake (Instance->TlsConn)) {
508 Status = TlsDoHandshake (
509 Instance->TlsConn,
510 RequestBuffer,
511 RequestSize,
512 Buffer,
513 BufferSize
514 );
515 if (EFI_ERROR (Status)) {
516 goto ON_EXIT;
517 }
518
519 if (!TlsInHandshake (Instance->TlsConn)) {
520 Instance->TlsSessionState = EfiTlsSessionDataTransferring;
521 }
522 } else {
523 //
524 // Must be alert message, Decrypt it and build the ResponsePacket.
525 //
526 ASSERT (((TLS_RECORD_HEADER *) RequestBuffer)->ContentType == TlsContentTypeAlert);
527
528 Status = TlsHandleAlert (
529 Instance->TlsConn,
530 RequestBuffer,
531 RequestSize,
532 Buffer,
533 BufferSize
534 );
535 if (EFI_ERROR (Status)) {
536 if (Status != EFI_BUFFER_TOO_SMALL) {
537 Instance->TlsSessionState = EfiTlsSessionError;
538 }
539
540 goto ON_EXIT;
541 }
542 }
543 }
544
545ON_EXIT:
546 gBS->RestoreTPL (OldTpl);
547 return Status;
548}
549
550/**
551 Decrypt or encrypt TLS packet during session. This function is only valid after
552 session connected and for application_data content type.
553
554 The ProcessPacket () function process each inbound or outbound TLS APP packet.
555
556 @param[in] This Pointer to the EFI_TLS_PROTOCOL instance.
557 @param[in, out] FragmentTable Pointer to a list of fragment. The caller will take
558 responsible to handle the original FragmentTable while
559 it may be reallocated in TLS driver. If CryptMode is
560 EfiTlsEncrypt, on input these fragments contain the TLS
561 header and plain text TLS APP payload; on output these
562 fragments contain the TLS header and cipher text TLS
563 APP payload. If CryptMode is EfiTlsDecrypt, on input
564 these fragments contain the TLS header and cipher text
565 TLS APP payload; on output these fragments contain the
566 TLS header and plain text TLS APP payload.
567 @param[in] FragmentCount Number of fragment.
568 @param[in] CryptMode Crypt mode.
569
570 @retval EFI_SUCCESS The operation completed successfully.
571 @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
572 This is NULL.
573 FragmentTable is NULL.
574 FragmentCount is NULL.
575 CryptoMode is invalid.
576 @retval EFI_NOT_READY Current TLS session state is NOT
577 EfiTlsSessionDataTransferring.
578 @retval EFI_ABORTED Something wrong decryption the message. TLS session
579 status will become EfiTlsSessionError. The caller need
580 call BuildResponsePacket() to generate Error Alert
581 message and send it out.
582 @retval EFI_OUT_OF_RESOURCES No enough resource to finish the operation.
583**/
584EFI_STATUS
585EFIAPI
586TlsProcessPacket (
587 IN EFI_TLS_PROTOCOL *This,
588 IN OUT EFI_TLS_FRAGMENT_DATA **FragmentTable,
589 IN UINT32 *FragmentCount,
590 IN EFI_TLS_CRYPT_MODE CryptMode
591 )
592{
593 EFI_STATUS Status;
594 TLS_INSTANCE *Instance;
595
596 EFI_TPL OldTpl;
597
598 Status = EFI_SUCCESS;
599
600 if (This == NULL || FragmentTable == NULL || FragmentCount == NULL) {
601 return EFI_INVALID_PARAMETER;
602 }
603
604 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
605
606 Instance = TLS_INSTANCE_FROM_PROTOCOL (This);
607
608 if (Instance->TlsSessionState != EfiTlsSessionDataTransferring) {
609 Status = EFI_NOT_READY;
610 goto ON_EXIT;
611 }
612
613 //
614 // Packet sent or received may have multiple TLS record messages (Application data type).
615 // So,on input these fragments contain the TLS header and TLS APP payload;
616 // on output these fragments also contain the TLS header and TLS APP payload.
617 //
618 switch (CryptMode) {
619 case EfiTlsEncrypt:
620 Status = TlsEncryptPacket (Instance, FragmentTable, FragmentCount);
621 break;
622 case EfiTlsDecrypt:
623 Status = TlsDecryptPacket (Instance, FragmentTable, FragmentCount);
624 break;
625 default:
626 return EFI_INVALID_PARAMETER;
627 }
628
629ON_EXIT:
630 gBS->RestoreTPL (OldTpl);
631 return Status;
632}
633
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette