blob: c506a5336f9fbeecf8a8f60d60b0c9ab88931288 [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07002/**
Alexander Afanasyev4c9a3d52017-01-03 17:45:19 -08003 * Copyright (c) 2013-2017 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
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080025namespace ndn {
26namespace ndnsec {
27
Yingdi Yub61f5402014-02-26 17:46:11 -080028class HttpException : public std::runtime_error
Yingdi Yue6bfab22014-02-06 16:01:19 -080029{
Yingdi Yub61f5402014-02-26 17:46:11 -080030public:
31 explicit
32 HttpException(const std::string& what)
33 : std::runtime_error(what)
Yingdi Yue6bfab22014-02-06 16:01:19 -080034 {
35 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080036};
37
Alexander Afanasyev35109a12017-01-04 15:39:06 -080038security::v2::Certificate
Yingdi Yub61f5402014-02-26 17:46:11 -080039getCertificateHttp(const std::string& host, const std::string& port, const std::string& path)
Yingdi Yue6bfab22014-02-06 16:01:19 -080040{
41 using namespace boost::asio::ip;
Yingdi Yub61f5402014-02-26 17:46:11 -080042 tcp::iostream requestStream;
43
44 requestStream.expires_from_now(boost::posix_time::milliseconds(3000));
45
46 requestStream.connect(host, port);
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080047 if (!requestStream) {
48 BOOST_THROW_EXCEPTION(HttpException("HTTP connection error"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070049 }
Yingdi Yub61f5402014-02-26 17:46:11 -080050 requestStream << "GET " << path << " HTTP/1.0\r\n";
51 requestStream << "Host: " << host << "\r\n";
52 requestStream << "Accept: */*\r\n";
53 requestStream << "Cache-Control: no-cache\r\n";
54 requestStream << "Connection: close\r\n\r\n";
55 requestStream.flush();
Yingdi Yue6bfab22014-02-06 16:01:19 -080056
Yingdi Yub61f5402014-02-26 17:46:11 -080057 std::string statusLine;
58 std::getline(requestStream, statusLine);
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080059 if (!requestStream) {
60 BOOST_THROW_EXCEPTION(HttpException("HTTP communication error"));
61 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080062
Yingdi Yub61f5402014-02-26 17:46:11 -080063 std::stringstream responseStream(statusLine);
64 std::string httpVersion;
65 responseStream >> httpVersion;
66 unsigned int statusCode;
67 responseStream >> statusCode;
68 std::string statusMessage;
Yingdi Yue6bfab22014-02-06 16:01:19 -080069
Yingdi Yub61f5402014-02-26 17:46:11 -080070 std::getline(responseStream, statusMessage);
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080071 if (!requestStream || httpVersion.substr(0, 5) != "HTTP/") {
72 BOOST_THROW_EXCEPTION(HttpException("HTTP communication error"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070073 }
74 if (statusCode != 200) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -080075 BOOST_THROW_EXCEPTION(HttpException("HTTP server error"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070076 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080077 std::string header;
Yingdi Yub61f5402014-02-26 17:46:11 -080078 while (std::getline(requestStream, header) && header != "\r")
79 ;
Yingdi Yue6bfab22014-02-06 16:01:19 -080080
Yingdi Yu8d7468f2014-02-21 14:49:45 -080081 ndn::OBufferStream os;
Yingdi Yub61f5402014-02-26 17:46:11 -080082 {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070083 using namespace ndn::security::transform;
84 streamSource(requestStream) >> base64Decode(true) >> streamSink(os);
Yingdi Yub61f5402014-02-26 17:46:11 -080085 }
Yingdi Yue6bfab22014-02-06 16:01:19 -080086
Alexander Afanasyev35109a12017-01-04 15:39:06 -080087 return security::v2::Certificate(Block(os.buf()));
Yingdi Yue6bfab22014-02-06 16:01:19 -080088}
89
Yingdi Yub61f5402014-02-26 17:46:11 -080090int
Yingdi Yu8d7468f2014-02-21 14:49:45 -080091ndnsec_cert_install(int argc, char** argv)
Yingdi Yue6bfab22014-02-06 16:01:19 -080092{
Yingdi Yu8d7468f2014-02-21 14:49:45 -080093 namespace po = boost::program_options;
94
95 std::string certFileName;
Yingdi Yub61f5402014-02-26 17:46:11 -080096 bool isSystemDefault = true;
97 bool isIdentityDefault = false;
98 bool isKeyDefault = false;
Yingdi Yue6bfab22014-02-06 16:01:19 -080099
Yingdi Yub61f5402014-02-26 17:46:11 -0800100 po::options_description description("General Usage\n ndnsec cert-install [-h] [-I|K|N] cert-file\nGeneral options");
101 description.add_options()
Yingdi Yue6bfab22014-02-06 16:01:19 -0800102 ("help,h", "produce help message")
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800103 ("cert-file,f", po::value<std::string>(&certFileName), "file name of the ceritificate, - for stdin. "
Yingdi Yue6bfab22014-02-06 16:01:19 -0800104 "If starts with http://, will try to fetch "
105 "the certificate using HTTP GET request")
106 ("identity-default,I", "optional, if specified, the certificate will be set as the default certificate of the identity")
107 ("key-default,K", "optional, if specified, the certificate will be set as the default certificate of the key")
108 ("no-default,N", "optional, if specified, the certificate will be simply installed")
109 ;
110 po::positional_options_description p;
111 p.add("cert-file", 1);
112
113 po::variables_map vm;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700114 try {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800115 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
116 po::notify(vm);
117 }
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700118 catch (const std::exception& e) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800119 std::cerr << "ERROR: " << e.what() << std::endl;
120 return 1;
121 }
Yingdi Yue6bfab22014-02-06 16:01:19 -0800122
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700123 if (vm.count("help") != 0) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800124 std::cerr << description << std::endl;
125 return 0;
126 }
Yingdi Yue6bfab22014-02-06 16:01:19 -0800127
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700128 if (vm.count("cert-file") == 0) {
129 std::cerr << "cert_file must be specified" << std::endl;
130 std::cerr << description << std::endl;
131 return 1;
132 }
133
134 if (vm.count("identity-default") != 0) {
135 isIdentityDefault = true;
136 isSystemDefault = false;
137 }
138 else if (vm.count("key-default") != 0) {
139 isKeyDefault = true;
140 isSystemDefault = false;
141 }
142 else if (vm.count("no-default") != 0) {
143 // noDefault = true;
144 isSystemDefault = false;
145 }
146
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800147 security::v2::Certificate cert;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700148
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800149 try {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700150 if (certFileName.find("http://") == 0) {
151 std::string host;
152 std::string port;
153 std::string path;
154
155 size_t pos = 7; // offset of "http://"
156 size_t posSlash = certFileName.find("/", pos);
157
158 if (posSlash == std::string::npos)
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800159 BOOST_THROW_EXCEPTION(HttpException("Request line is not correctly formatted"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700160
161 size_t posPort = certFileName.find(":", pos);
162
163 if (posPort != std::string::npos && posPort < posSlash) {
164 // port is specified
165 port = certFileName.substr(posPort + 1, posSlash - posPort - 1);
166 host = certFileName.substr(pos, posPort - pos);
167 }
168 else {
169 port = "80";
170 host = certFileName.substr(pos, posSlash - pos);
Yingdi Yue6bfab22014-02-06 16:01:19 -0800171 }
172
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800173 path = certFileName.substr(posSlash, certFileName.size() - posSlash);
Yingdi Yue6bfab22014-02-06 16:01:19 -0800174
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700175 cert = getCertificateHttp(host, port, path);
176 }
177 else {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800178 cert = loadCertificate(certFileName);
179 }
180 }
181 catch (const CannotLoadCertificate&) {
182 std::cerr << "ERROR: Cannot load the certificate " << certFileName << std::endl;
183 return 1;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700184 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800185
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800186 security::v2::KeyChain keyChain;
187 security::Identity id;
188 security::Key key;
189 try {
190 id = keyChain.getPib().getIdentity(cert.getIdentity());
191 key = id.getKey(cert.getKeyName());
192 }
193 catch (const security::Pib::Error& e) {
194 std::cerr << "ERROR: " << e.what() << std::endl;
195 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800196
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800197 keyChain.addCertificate(key, cert);
Yingdi Yub61f5402014-02-26 17:46:11 -0800198
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700199 if (isSystemDefault) {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800200 keyChain.setDefaultIdentity(id);
201 keyChain.setDefaultKey(id, key);
202 keyChain.setDefaultCertificate(key, cert);
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700203 }
204 else if (isIdentityDefault) {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800205 keyChain.setDefaultKey(id, key);
206 keyChain.setDefaultCertificate(key, cert);
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700207 }
208 else if (isKeyDefault) {
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800209 keyChain.setDefaultCertificate(key, cert);
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700210 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800211
Alexander Afanasyev35109a12017-01-04 15:39:06 -0800212 std::cerr << "OK: certificate with name [" << cert.getName().toUri() << "] "
213 << "has been successfully installed" << std::endl;
Yingdi Yub61f5402014-02-26 17:46:11 -0800214
215 return 0;
Yingdi Yue6bfab22014-02-06 16:01:19 -0800216}
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800217
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800218} // namespace ndnsec
219} // namespace ndn