blob: 72a82bd178f1186d30761b3ad3147b06b1186374 [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 Afanasyev82c359c2017-01-04 14:48:07 -080038shared_ptr<security::v1::IdentityCertificate>
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 Afanasyev82c359c2017-01-04 14:48:07 -080087 auto identityCertificate = std::make_shared<security::v1::IdentityCertificate>();
Yingdi Yu8d7468f2014-02-21 14:49:45 -080088 identityCertificate->wireDecode(ndn::Block(os.buf()));
Yingdi Yue6bfab22014-02-06 16:01:19 -080089
90 return identityCertificate;
91}
92
Yingdi Yub61f5402014-02-26 17:46:11 -080093int
Yingdi Yu8d7468f2014-02-21 14:49:45 -080094ndnsec_cert_install(int argc, char** argv)
Yingdi Yue6bfab22014-02-06 16:01:19 -080095{
Yingdi Yu8d7468f2014-02-21 14:49:45 -080096 using namespace ndn;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070097 using namespace ndn::security;
Yingdi Yu8d7468f2014-02-21 14:49:45 -080098 namespace po = boost::program_options;
99
100 std::string certFileName;
Yingdi Yub61f5402014-02-26 17:46:11 -0800101 bool isSystemDefault = true;
102 bool isIdentityDefault = false;
103 bool isKeyDefault = false;
Yingdi Yue6bfab22014-02-06 16:01:19 -0800104
Yingdi Yub61f5402014-02-26 17:46:11 -0800105 po::options_description description("General Usage\n ndnsec cert-install [-h] [-I|K|N] cert-file\nGeneral options");
106 description.add_options()
Yingdi Yue6bfab22014-02-06 16:01:19 -0800107 ("help,h", "produce help message")
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800108 ("cert-file,f", po::value<std::string>(&certFileName), "file name of the ceritificate, - for stdin. "
Yingdi Yue6bfab22014-02-06 16:01:19 -0800109 "If starts with http://, will try to fetch "
110 "the certificate using HTTP GET request")
111 ("identity-default,I", "optional, if specified, the certificate will be set as the default certificate of the identity")
112 ("key-default,K", "optional, if specified, the certificate will be set as the default certificate of the key")
113 ("no-default,N", "optional, if specified, the certificate will be simply installed")
114 ;
115 po::positional_options_description p;
116 p.add("cert-file", 1);
117
118 po::variables_map vm;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700119 try {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800120 po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm);
121 po::notify(vm);
122 }
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700123 catch (const std::exception& e) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800124 std::cerr << "ERROR: " << e.what() << std::endl;
125 return 1;
126 }
Yingdi Yue6bfab22014-02-06 16:01:19 -0800127
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700128 if (vm.count("help") != 0) {
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800129 std::cerr << description << std::endl;
130 return 0;
131 }
Yingdi Yue6bfab22014-02-06 16:01:19 -0800132
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700133 if (vm.count("cert-file") == 0) {
134 std::cerr << "cert_file must be specified" << std::endl;
135 std::cerr << description << std::endl;
136 return 1;
137 }
138
139 if (vm.count("identity-default") != 0) {
140 isIdentityDefault = true;
141 isSystemDefault = false;
142 }
143 else if (vm.count("key-default") != 0) {
144 isKeyDefault = true;
145 isSystemDefault = false;
146 }
147 else if (vm.count("no-default") != 0) {
148 // noDefault = true;
149 isSystemDefault = false;
150 }
151
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800152 shared_ptr<security::v1::IdentityCertificate> cert;
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700153
154 if (certFileName.find("http://") == 0) {
155 std::string host;
156 std::string port;
157 std::string path;
158
159 size_t pos = 7; // offset of "http://"
160 size_t posSlash = certFileName.find("/", pos);
161
162 if (posSlash == std::string::npos)
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800163 BOOST_THROW_EXCEPTION(HttpException("Request line is not correctly formatted"));
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700164
165 size_t posPort = certFileName.find(":", pos);
166
167 if (posPort != std::string::npos && posPort < posSlash) {
168 // port is specified
169 port = certFileName.substr(posPort + 1, posSlash - posPort - 1);
170 host = certFileName.substr(pos, posPort - pos);
171 }
172 else {
173 port = "80";
174 host = certFileName.substr(pos, posSlash - pos);
Yingdi Yue6bfab22014-02-06 16:01:19 -0800175 }
176
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800177 path = certFileName.substr(posSlash, certFileName.size() - posSlash);
Yingdi Yue6bfab22014-02-06 16:01:19 -0800178
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700179 cert = getCertificateHttp(host, port, path);
180 }
181 else {
182 cert = getIdentityCertificate(certFileName);
183 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800184
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800185 if (cert == nullptr)
Yingdi Yub61f5402014-02-26 17:46:11 -0800186 return 1;
187
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800188 security::v1::KeyChain keyChain;
Yingdi Yub61f5402014-02-26 17:46:11 -0800189
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700190 if (isSystemDefault) {
191 keyChain.addCertificateAsIdentityDefault(*cert);
192 Name keyName = cert->getPublicKeyName();
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800193 Name identity = keyName.getSubName(0, keyName.size() - 1);
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700194 keyChain.setDefaultIdentity(identity);
195 }
196 else if (isIdentityDefault) {
197 keyChain.addCertificateAsIdentityDefault(*cert);
198 }
199 else if (isKeyDefault) {
200 keyChain.addCertificateAsKeyDefault(*cert);
201 }
202 else {
203 keyChain.addCertificate(*cert);
204 }
Yingdi Yub61f5402014-02-26 17:46:11 -0800205
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800206 std::cerr << "OK: certificate with name [" << cert->getName().toUri()
207 << "] has been successfully installed" << std::endl;
Yingdi Yub61f5402014-02-26 17:46:11 -0800208
209 return 0;
Yingdi Yue6bfab22014-02-06 16:01:19 -0800210}
Yingdi Yu8d7468f2014-02-21 14:49:45 -0800211
Alexander Afanasyev82c359c2017-01-04 14:48:07 -0800212} // namespace ndnsec
213} // namespace ndn