blob: e83f41bb0f975e5d0f53e6478582a8e63b6dd749 [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 Pesavento5afbb0b2018-01-01 17:24:18 -050025#include <boost/date_time/posix_time/posix_time_duration.hpp>
26
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080027namespace ndn {
28namespace ndnsec {
29
Yingdi Yub61f5402014-02-26 17:46:11 -080030class HttpException : public std::runtime_error
Yingdi Yue6bfab22014-02-06 16:01:19 -080031{
Yingdi Yub61f5402014-02-26 17:46:11 -080032public:
33 explicit
34 HttpException(const std::string& what)
35 : std::runtime_error(what)
Yingdi Yue6bfab22014-02-06 16:01:19 -080036 {
37 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080038};
39
Alexander Afanasyev35109a12017-01-04 15:39:06 -080040security::v2::Certificate
Yingdi Yub61f5402014-02-26 17:46:11 -080041getCertificateHttp(const std::string& host, const std::string& port, const std::string& path)
Yingdi Yue6bfab22014-02-06 16:01:19 -080042{
43 using namespace boost::asio::ip;
Davide Pesavento5afbb0b2018-01-01 17:24:18 -050044
Yingdi Yub61f5402014-02-26 17:46:11 -080045 tcp::iostream requestStream;
Davide Pesavento5afbb0b2018-01-01 17:24:18 -050046 requestStream.expires_from_now(boost::posix_time::seconds(3));
Yingdi Yub61f5402014-02-26 17:46:11 -080047 requestStream.connect(host, port);
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080048 if (!requestStream) {
49 BOOST_THROW_EXCEPTION(HttpException("HTTP connection error"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070050 }
Yingdi Yub61f5402014-02-26 17:46:11 -080051 requestStream << "GET " << path << " HTTP/1.0\r\n";
52 requestStream << "Host: " << host << "\r\n";
53 requestStream << "Accept: */*\r\n";
54 requestStream << "Cache-Control: no-cache\r\n";
55 requestStream << "Connection: close\r\n\r\n";
56 requestStream.flush();
Yingdi Yue6bfab22014-02-06 16:01:19 -080057
Yingdi Yub61f5402014-02-26 17:46:11 -080058 std::string statusLine;
59 std::getline(requestStream, statusLine);
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080060 if (!requestStream) {
61 BOOST_THROW_EXCEPTION(HttpException("HTTP communication error"));
62 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080063
Yingdi Yub61f5402014-02-26 17:46:11 -080064 std::stringstream responseStream(statusLine);
65 std::string httpVersion;
66 responseStream >> httpVersion;
67 unsigned int statusCode;
68 responseStream >> statusCode;
69 std::string statusMessage;
Yingdi Yue6bfab22014-02-06 16:01:19 -080070
Yingdi Yub61f5402014-02-26 17:46:11 -080071 std::getline(responseStream, statusMessage);
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080072 if (!requestStream || httpVersion.substr(0, 5) != "HTTP/") {
73 BOOST_THROW_EXCEPTION(HttpException("HTTP communication error"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070074 }
75 if (statusCode != 200) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080076 BOOST_THROW_EXCEPTION(HttpException("HTTP server error"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070077 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080078 std::string header;
Yingdi Yub61f5402014-02-26 17:46:11 -080079 while (std::getline(requestStream, header) && header != "\r")
80 ;
Yingdi Yue6bfab22014-02-06 16:01:19 -080081
Yingdi Yu8d7468f2014-02-21 14:49:45 -080082 ndn::OBufferStream os;
Yingdi Yub61f5402014-02-26 17:46:11 -080083 {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070084 using namespace ndn::security::transform;
85 streamSource(requestStream) >> base64Decode(true) >> streamSink(os);
Yingdi Yub61f5402014-02-26 17:46:11 -080086 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080087
Alexander Afanasyev35109a12017-01-04 15:39:06 -080088 return security::v2::Certificate(Block(os.buf()));
Yingdi Yue6bfab22014-02-06 16:01:19 -080089}
90
Yingdi Yub61f5402014-02-26 17:46:11 -080091int
Yingdi Yu8d7468f2014-02-21 14:49:45 -080092ndnsec_cert_install(int argc, char** argv)
Yingdi Yue6bfab22014-02-06 16:01:19 -080093{
Yingdi Yu8d7468f2014-02-21 14:49:45 -080094 namespace po = boost::program_options;
95
96 std::string certFileName;
Yingdi Yub61f5402014-02-26 17:46:11 -080097 bool isSystemDefault = true;
98 bool isIdentityDefault = false;
99 bool isKeyDefault = false;
Yingdi Yue6bfab22014-02-06 16:01:19 -0800100
Yingdi Yub61f5402014-02-26 17:46:11 -0800101 po::options_description description("General Usage\n ndnsec cert-install [-h] [-I|K|N] cert-file\nGeneral options");
102 description.add_options()
Yingdi Yue6bfab22014-02-06 16:01:19 -0800103 ("help,h", "produce help message")
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800104 ("cert-file,f", po::value<std::string>(&certFileName), "file name of the ceritificate, - for stdin. "
Yingdi Yue6bfab22014-02-06 16:01:19 -0800105 "If starts with http://, will try to fetch "
106 "the certificate using HTTP GET request")
107 ("identity-default,I", "optional, if specified, the certificate will be set as the default certificate of the identity")
108 ("key-default,K", "optional, if specified, the certificate will be set as the default certificate of the key")
109 ("no-default,N", "optional, if specified, the certificate will be simply installed")
110 ;
111 po::positional_options_description p;
112 p.add("cert-file", 1);
113
114 po::variables_map vm;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700115 try {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800116 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
117 po::notify(vm);
118 }
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700119 catch (const std::exception& e) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800120 std::cerr << "ERROR: " << e.what() << std::endl;
121 return 1;
122 }
Yingdi Yue6bfab22014-02-06 16:01:19 -0800123
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700124 if (vm.count("help") != 0) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800125 std::cerr << description << std::endl;
126 return 0;
127 }
Yingdi Yue6bfab22014-02-06 16:01:19 -0800128
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700129 if (vm.count("cert-file") == 0) {
130 std::cerr << "cert_file must be specified" << std::endl;
131 std::cerr << description << std::endl;
132 return 1;
133 }
134
135 if (vm.count("identity-default") != 0) {
136 isIdentityDefault = true;
137 isSystemDefault = false;
138 }
139 else if (vm.count("key-default") != 0) {
140 isKeyDefault = true;
141 isSystemDefault = false;
142 }
143 else if (vm.count("no-default") != 0) {
144 // noDefault = true;
145 isSystemDefault = false;
146 }
147
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800148 security::v2::Certificate cert;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700149
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800150 try {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700151 if (certFileName.find("http://") == 0) {
152 std::string host;
153 std::string port;
154 std::string path;
155
156 size_t pos = 7; // offset of "http://"
157 size_t posSlash = certFileName.find("/", pos);
158
159 if (posSlash == std::string::npos)
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800160 BOOST_THROW_EXCEPTION(HttpException("Request line is not correctly formatted"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700161
162 size_t posPort = certFileName.find(":", pos);
163
164 if (posPort != std::string::npos && posPort < posSlash) {
165 // port is specified
166 port = certFileName.substr(posPort + 1, posSlash - posPort - 1);
167 host = certFileName.substr(pos, posPort - pos);
168 }
169 else {
170 port = "80";
171 host = certFileName.substr(pos, posSlash - pos);
Yingdi Yue6bfab22014-02-06 16:01:19 -0800172 }
173
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800174 path = certFileName.substr(posSlash, certFileName.size() - posSlash);
Yingdi Yue6bfab22014-02-06 16:01:19 -0800175
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700176 cert = getCertificateHttp(host, port, path);
177 }
178 else {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800179 cert = loadCertificate(certFileName);
180 }
181 }
182 catch (const CannotLoadCertificate&) {
183 std::cerr << "ERROR: Cannot load the certificate " << certFileName << std::endl;
184 return 1;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700185 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800186
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800187 security::v2::KeyChain keyChain;
188 security::Identity id;
189 security::Key key;
190 try {
191 id = keyChain.getPib().getIdentity(cert.getIdentity());
192 key = id.getKey(cert.getKeyName());
193 }
194 catch (const security::Pib::Error& e) {
195 std::cerr << "ERROR: " << e.what() << std::endl;
196 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800197
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800198 keyChain.addCertificate(key, cert);
Yingdi Yub61f5402014-02-26 17:46:11 -0800199
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700200 if (isSystemDefault) {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800201 keyChain.setDefaultIdentity(id);
202 keyChain.setDefaultKey(id, key);
203 keyChain.setDefaultCertificate(key, cert);
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700204 }
205 else if (isIdentityDefault) {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800206 keyChain.setDefaultKey(id, key);
207 keyChain.setDefaultCertificate(key, cert);
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700208 }
209 else if (isKeyDefault) {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800210 keyChain.setDefaultCertificate(key, cert);
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700211 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800212
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800213 std::cerr << "OK: certificate with name [" << cert.getName().toUri() << "] "
214 << "has been successfully installed" << std::endl;
Yingdi Yub61f5402014-02-26 17:46:11 -0800215
216 return 0;
Yingdi Yue6bfab22014-02-06 16:01:19 -0800217}
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800218
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800219} // namespace ndnsec
220} // namespace ndn