summaryrefslogtreecommitdiff
path: root/cpp/src/IceSSL/SingleCertificateVerifier.cpp
blob: b0b87f5ba8ffd8633b59850ba80c225c81642c9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// **********************************************************************
//
// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
//
// This copy of Ice is licensed to you under the terms described in the
// ICE_LICENSE file included in this distribution.
//
// **********************************************************************

#include <IceSSL/SingleCertificateVerifier.h>
#include <IceSSL/Convert.h>
#include <openssl/err.h>
#include <algorithm>
#include <iostream>

using namespace std;
using Ice::ByteSeq;

IceSSL::SingleCertificateVerifier::SingleCertificateVerifier(const ByteSeq& publicKey) :
                                           _publicKey(publicKey)
{
}

int
IceSSL::SingleCertificateVerifier::verify(int preVerifyOkay,
                                          X509_STORE_CTX* x509StoreContext,
                                          SSL* sslConnection)
{
    // For getting the CA certificate
    X509* trustedCert = 0;
    X509_OBJECT trustedObject;

    // Get the peer certificate offered by whoever we're talking to.
    X509* peerCertificate = x509StoreContext->cert;

    // We only bother to do the rest of this if we have something to verify.
    if(peerCertificate)
    {
        // Get the subject name (Not a memory leak, this is how this is used).
        X509_NAME* peerCertName = X509_get_subject_name(peerCertificate);

        // The Trusted Certificate by the same name.
        int retCode = X509_STORE_get_by_subject(x509StoreContext,
                                                X509_LU_X509,
                                                peerCertName,
                                                &trustedObject);

        switch(retCode)
        {
            case X509_LU_X509:
            {
                trustedCert = trustedObject.data.x509;
                break;
            }
    
            case X509_LU_RETRY:
            {
                // Log the error properly.
                X509err(X509_F_X509_VERIFY_CERT, X509_R_SHOULD_RETRY);

                // Drop through intended.
            }
    
            default :
            {
                // Regardless of error, if we can't look up the trusted
                // certificate, then we fail out.

                preVerifyOkay = 0;
                break;
            }
        }
    }

    // Compare, only if we have both.
    if(trustedCert)
    {
        ByteSeq peerByteSeq;
        toByteSeq(peerCertificate, peerByteSeq);

        ByteSeq trustedByteSeq;
        toByteSeq(trustedCert, trustedByteSeq);

        // The presented certificate must exactly match one that is in
        // the certificate store, and that must be the expected certificate.

        preVerifyOkay = (peerByteSeq == trustedByteSeq) &&
                        (_publicKey == peerByteSeq);

        X509_OBJECT_free_contents(&trustedObject);
    }

    return preVerifyOkay;
}

void
IceSSL::SingleCertificateVerifier::toByteSeq(X509* certificate,
                                             ByteSeq& certByteSeq)
{
    // Convert the X509 to a unsigned char buffer.
    unsigned int certSize = i2d_X509(certificate, 0);
    unsigned char* certBuffer = new unsigned char[certSize];
    unsigned char* certPtr = certBuffer;
    i2d_X509(certificate, &certPtr);

    // Yet another conversion to a ByteSeq (easy comparison this way).
    IceSSL::ucharToByteSeq(certBuffer, certSize, certByteSeq);
    delete []certBuffer;
}