1 /** @file
2   The mian interface of IPsec Protocol.
3 
4   Copyright (c) 2009 - 2011, 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 "IpSecConfigImpl.h"
17 #include "IpSecImpl.h"
18 
19 EFI_IPSEC2_PROTOCOL  mIpSecInstance = { IpSecProcess, NULL, TRUE };
20 
21 /**
22   Handles IPsec packet processing for inbound and outbound IP packets.
23 
24   The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet.
25   The behavior is that it can perform one of the following actions:
26   bypass the packet, discard the packet, or protect the packet.
27 
28   @param[in]      This             Pointer to the EFI_IPSEC2_PROTOCOL instance.
29   @param[in]      NicHandle        Instance of the network interface.
30   @param[in]      IpVersion        IPV4 or IPV6.
31   @param[in, out] IpHead           Pointer to the IP Header.
32   @param[in, out] LastHead         The protocol of the next layer to be processed by IPsec.
33   @param[in, out] OptionsBuffer    Pointer to the options buffer.
34   @param[in, out] OptionsLength    Length of the options buffer.
35   @param[in, out] FragmentTable    Pointer to a list of fragments.
36   @param[in, out] FragmentCount    Number of fragments.
37   @param[in]      TrafficDirection Traffic direction.
38   @param[out]     RecycleSignal    Event for recycling of resources.
39 
40   @retval EFI_SUCCESS              The packet was bypassed and all buffers remain the same.
41   @retval EFI_SUCCESS              The packet was protected.
42   @retval EFI_ACCESS_DENIED        The packet was discarded.
43 
44 **/
45 EFI_STATUS
46 EFIAPI
IpSecProcess(IN EFI_IPSEC2_PROTOCOL * This,IN EFI_HANDLE NicHandle,IN UINT8 IpVersion,IN OUT VOID * IpHead,IN OUT UINT8 * LastHead,IN OUT VOID ** OptionsBuffer,IN OUT UINT32 * OptionsLength,IN OUT EFI_IPSEC_FRAGMENT_DATA ** FragmentTable,IN OUT UINT32 * FragmentCount,IN EFI_IPSEC_TRAFFIC_DIR TrafficDirection,OUT EFI_EVENT * RecycleSignal)47 IpSecProcess (
48   IN     EFI_IPSEC2_PROTOCOL             *This,
49   IN     EFI_HANDLE                      NicHandle,
50   IN     UINT8                           IpVersion,
51   IN OUT VOID                            *IpHead,
52   IN OUT UINT8                           *LastHead,
53   IN OUT VOID                            **OptionsBuffer,
54   IN OUT UINT32                          *OptionsLength,
55   IN OUT EFI_IPSEC_FRAGMENT_DATA         **FragmentTable,
56   IN OUT UINT32                          *FragmentCount,
57   IN     EFI_IPSEC_TRAFFIC_DIR           TrafficDirection,
58      OUT EFI_EVENT                       *RecycleSignal
59   )
60 {
61   IPSEC_PRIVATE_DATA     *Private;
62   IPSEC_SPD_ENTRY        *SpdEntry;
63   EFI_IPSEC_SPD_SELECTOR *SpdSelector;
64   IPSEC_SAD_ENTRY        *SadEntry;
65   LIST_ENTRY             *SpdList;
66   LIST_ENTRY             *Entry;
67   EFI_IPSEC_ACTION       Action;
68   EFI_STATUS             Status;
69   UINT8                  *IpPayload;
70   UINT8                  OldLastHead;
71   BOOLEAN                IsOutbound;
72 
73   if (OptionsBuffer == NULL ||
74       OptionsLength == NULL ||
75       FragmentTable == NULL ||
76       FragmentCount == NULL
77       ) {
78     return EFI_INVALID_PARAMETER;
79   }
80   Private         = IPSEC_PRIVATE_DATA_FROM_IPSEC (This);
81   IpPayload       = (*FragmentTable)[0].FragmentBuffer;
82   IsOutbound      = (BOOLEAN) ((TrafficDirection == EfiIPsecOutBound) ? TRUE : FALSE);
83   OldLastHead     = *LastHead;
84   *RecycleSignal  = NULL;
85   SpdList         = &mConfigData[IPsecConfigDataTypeSpd];
86 
87   if (!IsOutbound) {
88     //
89     // For inbound traffic, process the ipsec header of the packet.
90     //
91     Status = IpSecProtectInboundPacket (
92               IpVersion,
93               IpHead,
94               LastHead,
95               OptionsBuffer,
96               OptionsLength,
97               FragmentTable,
98               FragmentCount,
99               &SpdSelector,
100               RecycleSignal
101               );
102 
103     if (Status == EFI_ACCESS_DENIED || Status == EFI_OUT_OF_RESOURCES) {
104       //
105       // The packet is denied to access.
106       //
107       goto ON_EXIT;
108     }
109 
110     if (Status == EFI_SUCCESS) {
111 
112       //
113       // Check the spd entry if the packet is accessible.
114       //
115       if (SpdSelector == NULL) {
116         Status = EFI_ACCESS_DENIED;
117         goto ON_EXIT;
118       }
119 
120       Status =  EFI_ACCESS_DENIED;
121       NET_LIST_FOR_EACH (Entry, SpdList) {
122         SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);
123         if (IsSubSpdSelector (
124               (EFI_IPSEC_CONFIG_SELECTOR *) SpdSelector,
125               (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector
126               )) {
127           Status = EFI_SUCCESS;
128         }
129       }
130       goto ON_EXIT;
131     }
132   }
133 
134   Status  = EFI_ACCESS_DENIED;
135 
136   NET_LIST_FOR_EACH (Entry, SpdList) {
137     //
138     // For outbound and non-ipsec Inbound traffic: check the spd entry.
139     //
140     SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry);
141 
142     if (EFI_ERROR (IpSecLookupSpdEntry (
143                      SpdEntry,
144                      IpVersion,
145                      IpHead,
146                      IpPayload,
147                      OldLastHead,
148                      IsOutbound,
149                      &Action
150                      ))) {
151       //
152       // If the related SPD not find
153       //
154       continue;
155     }
156 
157     switch (Action) {
158 
159     case EfiIPsecActionProtect:
160 
161       if (IsOutbound) {
162         //
163         // For outbound traffic, lookup the sad entry.
164         //
165         Status = IpSecLookupSadEntry (
166                    Private,
167                    NicHandle,
168                    IpVersion,
169                    IpHead,
170                    IpPayload,
171                    OldLastHead,
172                    SpdEntry,
173                    &SadEntry
174                    );
175 
176         if (SadEntry != NULL) {
177           //
178           // Process the packet by the found sad entry.
179           //
180           Status = IpSecProtectOutboundPacket (
181                     IpVersion,
182                     IpHead,
183                     LastHead,
184                     OptionsBuffer,
185                     OptionsLength,
186                     FragmentTable,
187                     FragmentCount,
188                     SadEntry,
189                     RecycleSignal
190                     );
191 
192         } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {
193           //
194           // TODO: if no need return not ready to upper layer, change here.
195           //
196           Status = EFI_SUCCESS;
197         }
198       } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) {
199         //
200         // For inbound icmpv6 traffic except ping request, accept the packet
201         // although no sad entry associated with protect spd entry.
202         //
203         Status = IpSecLookupSadEntry (
204                    Private,
205                    NicHandle,
206                    IpVersion,
207                    IpHead,
208                    IpPayload,
209                    OldLastHead,
210                    SpdEntry,
211                    &SadEntry
212                    );
213         if (SadEntry == NULL) {
214           Status = EFI_SUCCESS;
215         }
216       }
217 
218       goto ON_EXIT;
219 
220     case EfiIPsecActionBypass:
221       Status = EFI_SUCCESS;
222       goto ON_EXIT;
223 
224     case EfiIPsecActionDiscard:
225       goto ON_EXIT;
226     }
227   }
228 
229   //
230   // If don't find the related SPD entry, return the EFI_ACCESS_DENIED and discard it.
231   // But it the packet is NS/NA, it should be by passed even not find the related SPD entry.
232   //
233   if (OldLastHead == IP6_ICMP &&
234       (*IpPayload == ICMP_V6_NEIGHBOR_SOLICIT || *IpPayload == ICMP_V6_NEIGHBOR_ADVERTISE)
235       ){
236     Status = EFI_SUCCESS;
237   }
238 
239 ON_EXIT:
240   return Status;
241 }
242 
243