blob: cac0084907f08865c98da084ef10a3e9a1fb239f [file] [log] [blame]
Zhiyi Zhangdefa9592017-02-21 10:56:22 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -07003 * Copyright (c) 2017-2019, Regents of the University of California.
Zhiyi Zhangdefa9592017-02-21 10:56:22 -08004 *
5 * This file is part of ndncert, a certificate management system based on NDN.
6 *
7 * ndncert is free software: you can redistribute it and/or modify it under the terms
8 * of the GNU General Public License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
10 *
11 * ndncert 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 General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License along with
16 * ndncert, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * See AUTHORS.md for complete list of ndncert authors and contributors.
19 */
20
21#include "challenge-email.hpp"
Zhiyi Zhang46049832020-09-28 17:08:12 -070022
23#include <regex>
24
Zhiyi Zhang5f749a22019-06-12 17:02:33 -070025#include "../ca-module.hpp"
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080026#include "../logging.hpp"
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080027
28namespace ndn {
29namespace ndncert {
30
Zhiyi Zhang46049832020-09-28 17:08:12 -070031_LOG_INIT(ndncert.challenge.email);
Zhiyi Zhang36706832019-07-04 21:33:03 -070032NDNCERT_REGISTER_CHALLENGE(ChallengeEmail, "email");
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080033
34const std::string ChallengeEmail::NEED_CODE = "need-code";
35const std::string ChallengeEmail::WRONG_CODE = "wrong-code";
Zhiyi Zhang46049832020-09-28 17:08:12 -070036const std::string ChallengeEmail::PARAMETER_KEY_EMAIL = "email";
37const std::string ChallengeEmail::PARAMETER_KEY_CODE = "code";
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080038
39ChallengeEmail::ChallengeEmail(const std::string& scriptPath,
40 const size_t& maxAttemptTimes,
41 const time::seconds secretLifetime)
Zhiyi Zhang46049832020-09-28 17:08:12 -070042 : ChallengeModule("email")
43 , m_sendEmailScript(scriptPath)
44 , m_maxAttemptTimes(maxAttemptTimes)
45 , m_secretLifetime(secretLifetime)
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080046{
47}
48
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070049// For CA
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070050std::tuple<ErrorCode, std::string>
Suyong Won19fba4d2020-05-09 13:39:46 -070051ChallengeEmail::handleChallengeRequest(const Block& params, CertificateRequest& request)
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080052{
Suyong Won44d0cce2020-05-10 04:07:43 -070053 params.parse();
Zhiyi Zhang8da54d62019-11-21 00:03:05 -080054 auto currentTime = time::system_clock::now();
Zhiyi Zhang46049832020-09-28 17:08:12 -070055 if (request.m_status == Status::BEFORE_CHALLENGE) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070056 // for the first time, init the challenge
Suyong Won19fba4d2020-05-09 13:39:46 -070057 std::string emailAddress = readString(params.get(tlv_parameter_value));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070058 if (!isValidEmailAddress(emailAddress)) {
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070059 return returnWithError(request, ErrorCode::INVALID_PARAMETER, "Invalid email address format.");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070060 }
Zhiyi Zhang46049832020-09-28 17:08:12 -070061 auto lastComponentRequested = readString(request.m_cert.getIdentity().get(-1));
62 if (lastComponentRequested != emailAddress) {
63 _LOG_TRACE("Email and requested name do not match. Email " << emailAddress << "requested last component " << lastComponentRequested);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -070064 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070065 std::string emailCode = generateSecretCode();
66 JsonSection secretJson;
Zhiyi Zhang46049832020-09-28 17:08:12 -070067 secretJson.add(PARAMETER_KEY_CODE, emailCode);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070068 // send out the email
69 sendEmail(emailAddress, emailCode, request);
70 _LOG_TRACE("Secret for request " << request.m_requestId << " : " << emailCode);
Zhiyi Zhang46049832020-09-28 17:08:12 -070071 return returnWithNewChallengeStatus(request, NEED_CODE, std::move(secretJson), m_maxAttemptTimes, m_secretLifetime.count());
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080072 }
Zhiyi Zhang46049832020-09-28 17:08:12 -070073
74 if (request.m_challengeStatus == NEED_CODE || request.m_challengeStatus == WRONG_CODE) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070075 _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeStatus);
76 // the incoming interest should bring the pin code
Suyong Won19fba4d2020-05-09 13:39:46 -070077 std::string givenCode = readString(params.get(tlv_parameter_value));
Zhiyi Zhang46049832020-09-28 17:08:12 -070078 auto secret = request.m_challengeSecrets;
79 // check if run out of time
Zhiyi Zhang8da54d62019-11-21 00:03:05 -080080 if (currentTime - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070081 return returnWithError(request, ErrorCode::OUT_OF_TIME, "Secret expired.");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070082 }
Zhiyi Zhang46049832020-09-28 17:08:12 -070083 // check if provided secret is correct
84 if (givenCode == secret.get<std::string>(PARAMETER_KEY_CODE)) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070085 // the code is correct
Zhiyi Zhang46049832020-09-28 17:08:12 -070086 _LOG_TRACE("Correct secret code. Challenge succeeded.");
87 return returnWithSuccess(request);
88 }
89 // otherwise, check remaining attempt times
90 if (request.m_remainingTries > 1) {
91 auto remainTime = m_secretLifetime - (currentTime - time::fromIsoString(request.m_challengeTp));
92 _LOG_TRACE("Wrong secret code provided. Remaining Tries - 1.");
93 return returnWithNewChallengeStatus(request, WRONG_CODE, std::move(secret), request.m_remainingTries - 1, remainTime.count());
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080094 }
95 else {
Zhiyi Zhang46049832020-09-28 17:08:12 -070096 // run out times
97 _LOG_TRACE("Wrong secret code provided. Ran out tires. Challenge failed.");
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070098 return returnWithError(request, ErrorCode::OUT_OF_TRIES, "Ran out tires.");
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080099 }
100 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700101 return returnWithError(request, ErrorCode::INVALID_PARAMETER, "Unexpected status or challenge status");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700102}
103
104// For Client
Zhiyi Zhang46049832020-09-28 17:08:12 -0700105std::vector<std::tuple<std::string, std::string>>
106ChallengeEmail::getRequestedParameterList(Status status, const std::string& challengeStatus)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700107{
Zhiyi Zhang46049832020-09-28 17:08:12 -0700108 std::vector<std::tuple<std::string, std::string>> result;
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700109 if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
Zhiyi Zhang46049832020-09-28 17:08:12 -0700110 result.push_back(std::make_tuple(PARAMETER_KEY_EMAIL, "Please input your email address"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700111 }
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700112 else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
Zhiyi Zhang46049832020-09-28 17:08:12 -0700113 result.push_back(std::make_tuple(PARAMETER_KEY_CODE, "Please input your verification code"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700114 }
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700115 else if (status == Status::CHALLENGE && challengeStatus == WRONG_CODE) {
Zhiyi Zhang46049832020-09-28 17:08:12 -0700116 result.push_back(std::make_tuple(PARAMETER_KEY_CODE, "Incorrect code, please try again"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700117 }
118 else {
Zhiyi Zhang46049832020-09-28 17:08:12 -0700119 BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700120 }
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800121 return result;
122}
123
Suyong Won19fba4d2020-05-09 13:39:46 -0700124Block
Zhiyi Zhang46049832020-09-28 17:08:12 -0700125ChallengeEmail::genChallengeRequestTLV(Status status, const std::string& challengeStatus, std::vector<std::tuple<std::string, std::string>>&& params)
Suyong Won19fba4d2020-05-09 13:39:46 -0700126{
127 Block request = makeEmptyBlock(tlv_encrypted_payload);
Zhiyi Zhang46049832020-09-28 17:08:12 -0700128 if (status == Status::BEFORE_CHALLENGE) {
129 if (params.size() != 1 || std::get<0>(params[0]) != PARAMETER_KEY_EMAIL) {
130 BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
131 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700132 request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
Zhiyi Zhang46049832020-09-28 17:08:12 -0700133 request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_EMAIL));
134 request.push_back(makeStringBlock(tlv_parameter_value, std::get<1>(params[0])));
Suyong Won19fba4d2020-05-09 13:39:46 -0700135 }
Zhiyi Zhang46049832020-09-28 17:08:12 -0700136 else if (status == Status::CHALLENGE && (challengeStatus == NEED_CODE || challengeStatus == WRONG_CODE)) {
137 if (params.size() != 1 || std::get<0>(params[0]) != PARAMETER_KEY_CODE) {
138 BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
139 }
Suyong Won19fba4d2020-05-09 13:39:46 -0700140 request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
Zhiyi Zhang46049832020-09-28 17:08:12 -0700141 request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_CODE));
142 request.push_back(makeStringBlock(tlv_parameter_value, std::get<1>(params[0])));
Suyong Won19fba4d2020-05-09 13:39:46 -0700143 }
144 else {
Zhiyi Zhang46049832020-09-28 17:08:12 -0700145 BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
Suyong Won19fba4d2020-05-09 13:39:46 -0700146 }
Suyong Won44d0cce2020-05-10 04:07:43 -0700147 request.encode();
Suyong Won19fba4d2020-05-09 13:39:46 -0700148 return request;
149}
150
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800151bool
152ChallengeEmail::isValidEmailAddress(const std::string& emailAddress)
153{
Zhiyi Zhang8ce677b2018-07-13 14:44:06 -0700154 const std::string pattern = R"_REGEX_((^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9\-\.]+$))_REGEX_";
155 static const std::regex emailPattern(pattern);
156 return std::regex_match(emailAddress, emailPattern);
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800157}
158
159void
Zhiyi Zhang576aad12017-10-03 15:41:53 -0700160ChallengeEmail::sendEmail(const std::string& emailAddress, const std::string& secret,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700161 const CertificateRequest& request) const
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800162{
Zhiyi Zhang576aad12017-10-03 15:41:53 -0700163 std::string command = m_sendEmailScript;
Zhiyi Zhang46049832020-09-28 17:08:12 -0700164 command += " \"" + emailAddress + "\" \"" + secret + "\" \"" + request.m_caPrefix.toUri() + "\" \"" + request.m_cert.getName().toUri() + "\"";
Zhiyi Zhang576aad12017-10-03 15:41:53 -0700165 int result = system(command.c_str());
166 if (result == -1) {
167 _LOG_TRACE("EmailSending Script " + m_sendEmailScript + " fails.");
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800168 }
Zhiyi Zhang576aad12017-10-03 15:41:53 -0700169 _LOG_TRACE("EmailSending Script " + m_sendEmailScript +
170 " was executed successfully with return value" + std::to_string(result) + ".");
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800171 return;
172}
173
Zhiyi Zhang46049832020-09-28 17:08:12 -0700174} // namespace ndncert
175} // namespace ndn