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