XRootD
XrdSecztn.cc
Go to the documentation of this file.
1 // Copyright (c) 2015 Erwin Jansen
2 //
3 // MIT License
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 
24 // Note: the code in the anonymous namespace came from Erwin Jansen but was
25 // heavily edited to solve this particular problem. For more info see:
26 // https://github.com/pokowaka/jwt-cpp
27 
28 #include <cstdint>
29 #include <cstdlib>
30 #include <cstring>
31 #include <limits>
32 
33 #ifndef __FreeBSD__
34 #include <alloca.h>
35 #endif
36 
37 #define WHITESPACE 64
38 #define EQUALS 65
39 #define INVALID 66
40 
41 /******************************************************************************/
42 /* L o c a l F u n c t i o n s */
43 /******************************************************************************/
44 
45 namespace
46 {
47  const char b64Table[] = {
48  66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
49  66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
50  66, 66, 66, 66, 66, 66, 66, 62, 66, 62, 66, 63, 52, 53, 54, 55, 56, 57,
51  58, 59, 60, 61, 66, 66, 66, 66, 66, 66, 66, 0, 1, 2, 3, 4, 5, 6,
52  7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
53  25, 66, 66, 66, 66, 63, 66, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
54  37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 66, 66, 66,
55  66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
56  66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
57  66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
58  66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
59  66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
60  66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
61  66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
62  66, 66, 66, 66};
63 
64 /******************************************************************************/
65 /* D e c o d e B y t e s N e e d e d */
66 /******************************************************************************/
67 
74  size_t DecodeBytesNeeded(size_t num_decode) {
75  return 3 + (num_decode / 4) * 3;
76  }
77 
78 /******************************************************************************/
79 /* D e c o d e U r l */
80 /******************************************************************************/
81 
82 int DecodeUrl(const char *decode, size_t num_decode, char *out, size_t &num_out)
83 {
84  // No integer overflows please.
85  if (num_decode > std::numeric_limits<size_t>::max() - (size_t)decode)
86  return 1;
87 
88  if (num_out > std::numeric_limits<size_t>::max() - (size_t)out)
89  return 1;
90 
91  if (num_out < DecodeBytesNeeded(num_decode))
92  return 1;
93 
94  const char *end = decode + num_decode;
95  const char *out_start = out;
96  char iter = 0;
97  uint32_t buf = 0;
98  uint8_t ch;
99  char c;
100 
101  while (decode < end) {
102  ch = *decode++;
103  c = b64Table[ch];
104 
105  switch (c) {
106  case INVALID:
107  return 1; // invalid input, return error
108  default:
109  buf = buf << 6 | c;
110  iter++; // increment the number of iteration
111  // If the buffer is full, split it into bytes
112  if (iter == 4) {
113  *(out++) = (buf >> 16) & 0xff;
114  *(out++) = (buf >> 8) & 0xff;
115  *(out++) = buf & 0xff;
116  buf = 0;
117  iter = 0;
118  }
119  }
120  }
121 
122  if (iter == 3) {
123  *(out++) = (buf >> 10) & 0xff;
124  *(out++) = (buf >> 2) & 0xff;
125  } else {
126  if (iter == 2) {
127  *(out++) = (buf >> 4) & 0xff;
128  }
129  }
130 
131  num_out = (out - out_start); // modify to reflect the actual output size
132  return 0;
133 }
134 }
135 
136 /******************************************************************************/
137 /* X r d S e c z t n : : i s J W T */
138 /******************************************************************************/
139 
140 namespace XrdSecztn
141 {
142 bool isJWT(const char *b64data)
143 {
144  size_t inBytes, outBytes;
145  const char *dot;
146  char *key, *outData, inData[1024];
147 
148 // Skip over the header should it exist (sommetime it does sometimes not)
149 //
150  if (!strncmp(b64data, "Bearer%20", 9)) b64data += 9;
151 
152 // We are only interested in the header which must appear first and be
153 // separated by a dot from subsequent tokens. If it does not have the
154 // dot then we assume it's not returnable. Otherwise truncate it at the dot.
155 //
156  if (!(dot = index(b64data, '.'))) return false;
157 
158 // Copy out the token segment we wish to check. The JWT header can never be
159 // more than 1K long and that's being way generous.
160 //
161  inBytes = dot - b64data;
162  if (inBytes >= (int)sizeof(inData)) return false;
163  memcpy(inData, b64data, inBytes);
164  inData[inBytes] = 0;
165 
166 // Allocate a buffer large enough to hold the result. Get it from the stack.
167 //
168  outBytes = DecodeBytesNeeded(inBytes);
169  outData = (char *)alloca(outBytes);
170 
171 // If we can't decode what we have then indicate this is not returnable
172 //
173  if (DecodeUrl(inData, inBytes, outData, outBytes)) return false;
174 
175 // The json object must start/end with a brace and must contain the key:value
176 // of '"typ":"JWT"', other elements may change but not this one.
177 //
178  if (outBytes <= 0 || *outData != '{' || outData[outBytes-1] != '}')
179  return false;
180 
181 // Search for the key
182 //
183  if (!(key = strstr(outData, "\"typ\""))) return false;
184 
185 // Subsequently there should be a colon or spaces but nothing more
186 //
187  key += 5;
188  while(*key == ' ') key++;
189  if (*key != ':') return false;
190 
191 // There may be more spaces but anything else must be the expected value
192 //
193  key++;
194  while(*key == ' ') key++;
195  return strncmp(key, "\"JWT\"", 5) == 0;
196 }
197 }
#define INVALID
Definition: XrdSecztn.cc:39
bool isJWT(const char *)
Definition: XrdSecztn.cc:142