XRootD
XrdCryptogsiX509Chain.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d C r y p t o g s i X 5 0 9 C h a i n . c c */
4 /* */
5 /* (c) 2014 G. Ganis, CERN */
6 /* */
7 /* This file is part of the XRootD software suite. */
8 /* */
9 /* XRootD is free software: you can redistribute it and/or modify it under */
10 /* the terms of the GNU Lesser General Public License as published by the */
11 /* Free Software Foundation, either version 3 of the License, or (at your */
12 /* option) any later version. */
13 /* */
14 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
15 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
16 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
17 /* License for more details. */
18 /* */
19 /* You should have received a copy of the GNU Lesser General Public License */
20 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
21 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
22 /* */
23 /* The copyright holder's institutional names and contributor's names may not */
24 /* be used to endorse or promote products derived from this software without */
25 /* specific prior written permission of the institution or contributor. */
26 /* */
27 /******************************************************************************/
28 
29 #include <cstring>
30 #include <ctime>
31 
35 
36 // ---------------------------------------------------------------------------//
37 // //
38 // XrdCryptogsiX509Chain (was XrdCryptosslgsiX509Chain) //
39 // //
40 // Enforce GSI policies on X509 certificate chains //
41 // //
42 // ---------------------------------------------------------------------------//
43 
44 //___________________________________________________________________________
46 {
47  // Verify the chain
48  EPNAME("X509Chain::Verify");
49  errcode = kNone;
50 
51  // There must be at least a CA and a { EEC or subCA }.
52  if (size < 2) {
53  DEBUG("Nothing to verify (size: "<<size<<")");
54  return 0;
55  }
56  if (QTRACE(Dump)) { Dump(); }
57 
58  //
59  // Reorder if needed
60  if (Reorder() != 0) {
61  errcode = kInconsistent;
62  lastError = ":";
63  lastError += X509ChainError(errcode);
64  return 0;
65  }
66 
67  //
68  // Verification options
69  int opt = (vopt) ? vopt->opt : 0;
70  int when = (vopt) ? vopt->when : (int)time(0);
71  int plen = (vopt) ? vopt->pathlen : -1;
72  XrdCryptoX509Crl *crl = (vopt) ? vopt->crl : 0;
73 
74  //
75  // Global path depth length consistency check
76  if (plen > -1 && plen < size) {
77  errcode = kTooMany;
78  lastError = "checking path depth: ";
79  lastError += X509ChainError(errcode);
80  }
81 
82  //
83  // Check the first certificate: it MUST be of CA type, valid,
84  // self-signed
86  XrdCryptoX509 *xcer = node->Cert(); // Certificate under exam
87  XrdCryptoX509 *xsig = xcer; // Signing certificate
88  if (statusCA == kUnknown) {
89  if (!XrdCryptoX509Chain::Verify(errcode, "CA: ",
90  XrdCryptoX509::kCA, when, xcer, xsig))
91  return 0;
92  statusCA = kValid;
93  } else if (statusCA == kAbsent || statusCA == kInvalid) {
94  errcode = kNoCA;
95  lastError = X509ChainError(errcode);
96  return 0;
97  }
98 
99  //
100  // Update the max path depth len
101  if (plen > -1)
102  plen -= 1;
103  //
104  // Check sub-CA's certificate, if any
105  while (node->Next() && node->Next()->Cert()->type == XrdCryptoX509::kCA) {
106  xsig = xcer;
107  node = node->Next();
108  xcer = node->Cert();
109  if (!XrdCryptoX509Chain::Verify(errcode, "Sub-CA: ",
111  when, xcer, xsig, crl))
112  return 0;
113  //
114  // Update the max path depth len
115  if (plen > -1)
116  plen -= 1;
117  }
118 
119  //
120  // If subCA verification case we are done
121  if (opt & kOptsCheckSubCA) return 1;
122 
123  //
124  // Check the end-point entity certificate
125  if (!node->Next() || // We expect somethign else if not in subCA checking mode
126  (node->Next() && node->Next()->Cert()->type != XrdCryptoX509::kEEC)) {
127  errcode = kNoEEC;
128  lastError = X509ChainError(errcode);
129  return 0;
130  }
131 
132  //
133  // Check the end-point entity certificate
134  xsig = xcer;
135  node = node->Next();
136  xcer = node->Cert();
137  if (!XrdCryptoX509Chain::Verify(errcode, "EEC: ",
139  when, xcer, xsig, crl))
140  return 0;
141  //
142  // Update the max path depth len
143  if (plen > -1)
144  plen -= 1;
145 
146  //
147  // Only one end-point entity certificate
148  if (node->Next() && node->Next()->Cert()->type == XrdCryptoX509::kEEC) {
149  errcode = kTooManyEEC;
150  lastError = X509ChainError(errcode);
151  return 0;
152  }
153 
154  //
155  // There are proxy certificates
156  xsig = xcer;
157  node = node->Next();
158  while (node && (plen == -1 || plen > 0)) {
159 
160  // Attache to certificate
161  xcer = node->Cert();
162 
163  //
164  // Must be a recognized proxy certificate
165  if (xcer && xcer->type != XrdCryptoX509::kProxy) {
166  errcode = kInvalidProxy;
167  lastError = X509ChainError(errcode);
168  return 0;
169  }
170 
171  // Proxy subject name must follow some rules
172  if (!SubjectOK(errcode, xcer))
173  return 0;
174 
175  // Check if ProxyCertInfo extension is there (required by RFC3820)
176  int pxplen = -1; bool b;
177  if (opt & kOptsRfc3820) {
178  const void *extdata = xcer->GetExtension(gsiProxyCertInfo_OID);
179  if (!extdata) extdata = xcer->GetExtension(gsiProxyCertInfo_OLD_OID);
180  if (!extdata || !cfact || !(cfact && (*(cfact->ProxyCertInfo()))(extdata, pxplen, &b))) {
181  errcode = kMissingExtension;
182  lastError = "rfc3820: ";
183  lastError += X509ChainError(errcode);
184  return 0;
185  }
186  }
187  // Update plen, if needed
188  if (plen == -1) {
189  plen = (pxplen > -1) ? pxplen : plen;
190  } else {
191  plen--;
192  // Aply stricter rules if required
193  plen = (pxplen > -1 && pxplen < plen) ? pxplen : plen;
194  }
195 
196  // Standard verification
197  if (!XrdCryptoX509Chain::Verify(errcode, "Proxy: ",
198  XrdCryptoX509::kProxy, when, xcer, xsig))
199  return 0;
200 
201  // Get next
202  xsig = xcer;
203  node = node->Next();
204  }
205 
206  // We are done (successfully!)
207  return 1;
208 }
209 
210 //___________________________________________________________________________
211 bool XrdCryptogsiX509Chain::SubjectOK(EX509ChainErr &errcode, XrdCryptoX509 *xcer)
212 {
213  // Apply GSI rules for proxy subject names
214 
215  // Check inputs
216  if (!xcer) {
217  errcode = kNoCertificate;
218  lastError = "subject check:";
219  lastError += X509ChainError(errcode);
220  return 0;
221  }
222 
223  // This applies only to proxies
224  if (xcer->type != XrdCryptoX509::kProxy)
225  return 1;
226 
227  // Pointers to names
228  if (!(xcer->Subject()) || !(xcer->Issuer())) {
229  errcode = kInvalidNames;
230  lastError = "subject check:";
231  lastError += X509ChainError(errcode);
232  return 0;
233  }
234 
235  // Subject name must start with issuer name.
236  // We need the length of the common part between issuer and subject.
237  // We allow proxies issued by other proxies. In such cases we must
238  // ignore the last '/CN=' in the issuer name; this explains the need
239  // for the following gymnastic.
240 
241  int ilen = strlen(xcer->Issuer());
242  if (strncmp(xcer->Subject(), xcer->Issuer(), ilen)) {
243  // Check if the issuer is a proxy: ignore the last 'CN='
244  char *pcn = (char *) strstr(xcer->Issuer(), "/CN=");
245  if (pcn) {
246  char *pcnn = 0;
247  while ((pcnn = (char *) strstr(pcn+1,"/CN=")))
248  pcn = pcnn;
249  ilen = (int)(pcn - xcer->Issuer());
250  }
251  if (strncmp(xcer->Subject() + ilen,"/CN=",4)) {
252  errcode = kInvalidNames;
253  lastError = "proxy subject check: found additional chars :";
254  lastError += X509ChainError(errcode);
255  return 0;
256  }
257  if (strncmp(xcer->Subject(), xcer->Issuer(), ilen)) {
258  errcode = kInvalidNames;
259  lastError = "proxy issuer check: issuer not found in subject :";
260  lastError += X509ChainError(errcode);
261  return 0;
262  }
263  }
264 
265  // A common name must be appendend
266  char *pp = (char *)strstr(xcer->Subject()+ilen, "CN=");
267  if (!pp) {
268  errcode = kInvalidNames;
269  lastError = "proxy subject check: no appended 'CN='";
270  lastError += X509ChainError(errcode);
271  return 0;
272  }
273 
274  // But only one
275  pp = strstr(pp+strlen("CN="), "CN=");
276  if (pp) {
277  errcode = kInvalidNames;
278  lastError = "proxy subject check: too many appended 'CN='s";
279  lastError += X509ChainError(errcode);
280  return 0;
281  }
282 
283  // We are done
284  return 1;
285 }
286 
287 
#define DEBUG(x)
Definition: XrdBwmTrace.hh:54
#define EPNAME(x)
Definition: XrdBwmTrace.hh:56
#define QTRACE(act)
Definition: XrdCmsTrace.hh:49
#define gsiProxyCertInfo_OID
#define gsiProxyCertInfo_OLD_OID
XrdCryptoX509Crl * crl
const int kOptsCheckSubCA
const int kOptsRfc3820
virtual XrdCryptoProxyCertInfo_t ProxyCertInfo()
XrdCryptoX509 * Cert() const
XrdCryptoX509ChainNode * Next() const
virtual bool Verify(EX509ChainErr &e, x509ChainVerifyOpt_t *vopt=0)
XrdCryptoX509ChainNode * begin
const char * X509ChainError(EX509ChainErr e)
virtual XrdCryptoX509data GetExtension(const char *oid)
virtual const char * Subject()
virtual const char * Issuer()
EX509Type type
bool Verify(EX509ChainErr &e, x509ChainVerifyOpt_t *vopt=0)