XRootD
Loading...
Searching...
No Matches
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;
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//___________________________________________________________________________
211bool 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)
#define EPNAME(x)
#define QTRACE(act)
#define gsiProxyCertInfo_OID
#define gsiProxyCertInfo_OLD_OID
XrdCryptoX509Crl * crl
const int kOptsCheckSubCA
const int kOptsRfc3820
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()
bool Verify(EX509ChainErr &e, x509ChainVerifyOpt_t *vopt=0)