blob: 3ae67248b60af074ef97e3b551a265b48f1f4008 [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesavento5afbb0b2018-01-01 17:24:18 -05002/*
3 * Copyright (c) 2013-2018 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
Yingdi Yue6bfab22014-02-06 16:01:19 -080020 */
21
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080022#include "ndnsec.hpp"
Alexander Afanasyevd7db8bf2015-01-04 15:31:02 -080023#include "util.hpp"
Yingdi Yue6bfab22014-02-06 16:01:19 -080024
Davide Pesavento6d433932018-04-17 18:35:00 -040025#if BOOST_VERSION < 106700
Davide Pesavento5afbb0b2018-01-01 17:24:18 -050026#include <boost/date_time/posix_time/posix_time_duration.hpp>
Davide Pesavento6d433932018-04-17 18:35:00 -040027#endif // BOOST_VERSION < 106700
Davide Pesavento5afbb0b2018-01-01 17:24:18 -050028
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080029namespace ndn {
30namespace ndnsec {
31
Yingdi Yub61f5402014-02-26 17:46:11 -080032class HttpException : public std::runtime_error
Yingdi Yue6bfab22014-02-06 16:01:19 -080033{
Yingdi Yub61f5402014-02-26 17:46:11 -080034public:
35 explicit
36 HttpException(const std::string& what)
37 : std::runtime_error(what)
Yingdi Yue6bfab22014-02-06 16:01:19 -080038 {
39 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080040};
41
Alexander Afanasyev35109a12017-01-04 15:39:06 -080042security::v2::Certificate
Yingdi Yub61f5402014-02-26 17:46:11 -080043getCertificateHttp(const std::string& host, const std::string& port, const std::string& path)
Yingdi Yue6bfab22014-02-06 16:01:19 -080044{
Davide Pesavento6d433932018-04-17 18:35:00 -040045 boost::asio::ip::tcp::iostream requestStream;
46#if BOOST_VERSION >= 106700
47 requestStream.expires_after(std::chrono::seconds(3));
48#else
Davide Pesavento5afbb0b2018-01-01 17:24:18 -050049 requestStream.expires_from_now(boost::posix_time::seconds(3));
Davide Pesavento6d433932018-04-17 18:35:00 -040050#endif // BOOST_VERSION >= 106700
51
Yingdi Yub61f5402014-02-26 17:46:11 -080052 requestStream.connect(host, port);
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080053 if (!requestStream) {
54 BOOST_THROW_EXCEPTION(HttpException("HTTP connection error"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070055 }
Davide Pesavento6d433932018-04-17 18:35:00 -040056
Yingdi Yub61f5402014-02-26 17:46:11 -080057 requestStream << "GET " << path << " HTTP/1.0\r\n";
58 requestStream << "Host: " << host << "\r\n";
59 requestStream << "Accept: */*\r\n";
60 requestStream << "Cache-Control: no-cache\r\n";
61 requestStream << "Connection: close\r\n\r\n";
62 requestStream.flush();
Yingdi Yue6bfab22014-02-06 16:01:19 -080063
Yingdi Yub61f5402014-02-26 17:46:11 -080064 std::string statusLine;
65 std::getline(requestStream, statusLine);
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080066 if (!requestStream) {
67 BOOST_THROW_EXCEPTION(HttpException("HTTP communication error"));
68 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080069
Yingdi Yub61f5402014-02-26 17:46:11 -080070 std::stringstream responseStream(statusLine);
71 std::string httpVersion;
72 responseStream >> httpVersion;
73 unsigned int statusCode;
74 responseStream >> statusCode;
75 std::string statusMessage;
Yingdi Yue6bfab22014-02-06 16:01:19 -080076
Yingdi Yub61f5402014-02-26 17:46:11 -080077 std::getline(responseStream, statusMessage);
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080078 if (!requestStream || httpVersion.substr(0, 5) != "HTTP/") {
79 BOOST_THROW_EXCEPTION(HttpException("HTTP communication error"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070080 }
81 if (statusCode != 200) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080082 BOOST_THROW_EXCEPTION(HttpException("HTTP server error"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070083 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080084 std::string header;
Yingdi Yub61f5402014-02-26 17:46:11 -080085 while (std::getline(requestStream, header) && header != "\r")
86 ;
Yingdi Yue6bfab22014-02-06 16:01:19 -080087
Yingdi Yu8d7468f2014-02-21 14:49:45 -080088 ndn::OBufferStream os;
Yingdi Yub61f5402014-02-26 17:46:11 -080089 {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070090 using namespace ndn::security::transform;
91 streamSource(requestStream) >> base64Decode(true) >> streamSink(os);
Yingdi Yub61f5402014-02-26 17:46:11 -080092 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080093
Alexander Afanasyev35109a12017-01-04 15:39:06 -080094 return security::v2::Certificate(Block(os.buf()));
Yingdi Yue6bfab22014-02-06 16:01:19 -080095}
96
Yingdi Yub61f5402014-02-26 17:46:11 -080097int
Yingdi Yu8d7468f2014-02-21 14:49:45 -080098ndnsec_cert_install(int argc, char** argv)
Yingdi Yue6bfab22014-02-06 16:01:19 -080099{
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800100 namespace po = boost::program_options;
101
102 std::string certFileName;
Yingdi Yub61f5402014-02-26 17:46:11 -0800103 bool isSystemDefault = true;
104 bool isIdentityDefault = false;
105 bool isKeyDefault = false;
Yingdi Yue6bfab22014-02-06 16:01:19 -0800106
Yingdi Yub61f5402014-02-26 17:46:11 -0800107 po::options_description description("General Usage\n ndnsec cert-install [-h] [-I|K|N] cert-file\nGeneral options");
108 description.add_options()
Yingdi Yue6bfab22014-02-06 16:01:19 -0800109 ("help,h", "produce help message")
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800110 ("cert-file,f", po::value<std::string>(&certFileName), "file name of the ceritificate, - for stdin. "
Yingdi Yue6bfab22014-02-06 16:01:19 -0800111 "If starts with http://, will try to fetch "
112 "the certificate using HTTP GET request")
113 ("identity-default,I", "optional, if specified, the certificate will be set as the default certificate of the identity")
114 ("key-default,K", "optional, if specified, the certificate will be set as the default certificate of the key")
115 ("no-default,N", "optional, if specified, the certificate will be simply installed")
116 ;
117 po::positional_options_description p;
118 p.add("cert-file", 1);
119
120 po::variables_map vm;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700121 try {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800122 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
123 po::notify(vm);
124 }
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700125 catch (const std::exception& e) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800126 std::cerr << "ERROR: " << e.what() << std::endl;
127 return 1;
128 }
Yingdi Yue6bfab22014-02-06 16:01:19 -0800129
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700130 if (vm.count("help") != 0) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800131 std::cerr << description << std::endl;
132 return 0;
133 }
Yingdi Yue6bfab22014-02-06 16:01:19 -0800134
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700135 if (vm.count("cert-file") == 0) {
136 std::cerr << "cert_file must be specified" << std::endl;
137 std::cerr << description << std::endl;
138 return 1;
139 }
140
141 if (vm.count("identity-default") != 0) {
142 isIdentityDefault = true;
143 isSystemDefault = false;
144 }
145 else if (vm.count("key-default") != 0) {
146 isKeyDefault = true;
147 isSystemDefault = false;
148 }
149 else if (vm.count("no-default") != 0) {
150 // noDefault = true;
151 isSystemDefault = false;
152 }
153
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800154 security::v2::Certificate cert;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700155
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800156 try {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700157 if (certFileName.find("http://") == 0) {
158 std::string host;
159 std::string port;
160 std::string path;
161
162 size_t pos = 7; // offset of "http://"
163 size_t posSlash = certFileName.find("/", pos);
164
165 if (posSlash == std::string::npos)
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800166 BOOST_THROW_EXCEPTION(HttpException("Request line is not correctly formatted"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700167
168 size_t posPort = certFileName.find(":", pos);
169
170 if (posPort != std::string::npos && posPort < posSlash) {
171 // port is specified
172 port = certFileName.substr(posPort + 1, posSlash - posPort - 1);
173 host = certFileName.substr(pos, posPort - pos);
174 }
175 else {
176 port = "80";
177 host = certFileName.substr(pos, posSlash - pos);
Yingdi Yue6bfab22014-02-06 16:01:19 -0800178 }
179
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800180 path = certFileName.substr(posSlash, certFileName.size() - posSlash);
Yingdi Yue6bfab22014-02-06 16:01:19 -0800181
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700182 cert = getCertificateHttp(host, port, path);
183 }
184 else {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800185 cert = loadCertificate(certFileName);
186 }
187 }
188 catch (const CannotLoadCertificate&) {
189 std::cerr << "ERROR: Cannot load the certificate " << certFileName << std::endl;
190 return 1;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700191 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800192
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800193 security::v2::KeyChain keyChain;
194 security::Identity id;
195 security::Key key;
196 try {
197 id = keyChain.getPib().getIdentity(cert.getIdentity());
198 key = id.getKey(cert.getKeyName());
199 }
200 catch (const security::Pib::Error& e) {
201 std::cerr << "ERROR: " << e.what() << std::endl;
202 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800203
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800204 keyChain.addCertificate(key, cert);
Yingdi Yub61f5402014-02-26 17:46:11 -0800205
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700206 if (isSystemDefault) {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800207 keyChain.setDefaultIdentity(id);
208 keyChain.setDefaultKey(id, key);
209 keyChain.setDefaultCertificate(key, cert);
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700210 }
211 else if (isIdentityDefault) {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800212 keyChain.setDefaultKey(id, key);
213 keyChain.setDefaultCertificate(key, cert);
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700214 }
215 else if (isKeyDefault) {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800216 keyChain.setDefaultCertificate(key, cert);
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700217 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800218
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800219 std::cerr << "OK: certificate with name [" << cert.getName().toUri() << "] "
220 << "has been successfully installed" << std::endl;
Yingdi Yub61f5402014-02-26 17:46:11 -0800221
222 return 0;
Yingdi Yue6bfab22014-02-06 16:01:19 -0800223}
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800224
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800225} // namespace ndnsec
226} // namespace ndn