blob: 8b08d829499ccbd10574d7ce01fd10f667e8088e [file] [log] [blame]
Zhiyi Zhangdefa9592017-02-21 10:56:22 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Zhiyi Zhang74c61142020-10-07 21:00:49 -07003 * Copyright (c) 2017-2020, 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 Zhang523f0c22020-09-29 14:19:20 -070022#include <regex>
tylerliu1f480be2020-11-10 13:02:53 -080023#include <boost/process.hpp>
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080024
25namespace ndn {
26namespace ndncert {
27
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -070028NDN_LOG_INIT(ndncert.challenge.email);
Zhiyi Zhang36706832019-07-04 21:33:03 -070029NDNCERT_REGISTER_CHALLENGE(ChallengeEmail, "email");
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080030
31const std::string ChallengeEmail::NEED_CODE = "need-code";
32const std::string ChallengeEmail::WRONG_CODE = "wrong-code";
Zhiyi Zhangead9f002020-10-03 15:42:52 -070033const std::string ChallengeEmail::INVALID_EMAIL = "invalid-email";
Zhiyi Zhang46049832020-09-28 17:08:12 -070034const std::string ChallengeEmail::PARAMETER_KEY_EMAIL = "email";
35const std::string ChallengeEmail::PARAMETER_KEY_CODE = "code";
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080036
37ChallengeEmail::ChallengeEmail(const std::string& scriptPath,
38 const size_t& maxAttemptTimes,
39 const time::seconds secretLifetime)
Zhiyi Zhangead9f002020-10-03 15:42:52 -070040 : ChallengeModule("email", maxAttemptTimes, secretLifetime)
Zhiyi Zhang46049832020-09-28 17:08:12 -070041 , m_sendEmailScript(scriptPath)
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080042{
43}
44
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070045// For CA
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -070046std::tuple<ErrorCode, std::string>
Zhiyi Zhang32d4b4e2020-10-28 22:10:49 -070047ChallengeEmail::handleChallengeRequest(const Block& params, ca::RequestState& request)
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080048{
Suyong Won44d0cce2020-05-10 04:07:43 -070049 params.parse();
Zhiyi Zhang8da54d62019-11-21 00:03:05 -080050 auto currentTime = time::system_clock::now();
Zhiyi Zhang46049832020-09-28 17:08:12 -070051 if (request.m_status == Status::BEFORE_CHALLENGE) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070052 // for the first time, init the challenge
tylerliu50d679e2020-10-14 14:08:39 -070053 std::string emailAddress = readString(params.get(tlv::ParameterValue));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070054 if (!isValidEmailAddress(emailAddress)) {
Zhiyi Zhangead9f002020-10-03 15:42:52 -070055 return returnWithNewChallengeStatus(request, INVALID_EMAIL, JsonSection(), m_maxAttemptTimes - 1, m_secretLifetime);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070056 }
Zhiyi Zhang46049832020-09-28 17:08:12 -070057 auto lastComponentRequested = readString(request.m_cert.getIdentity().get(-1));
58 if (lastComponentRequested != emailAddress) {
tylerliu6563f932020-10-30 11:13:38 -070059 NDN_LOG_TRACE("Email and requested name do not match. Email " << emailAddress << "requested last component "
60 << lastComponentRequested);
Zhiyi Zhang5f749a22019-06-12 17:02:33 -070061 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070062 std::string emailCode = generateSecretCode();
63 JsonSection secretJson;
Zhiyi Zhang46049832020-09-28 17:08:12 -070064 secretJson.add(PARAMETER_KEY_CODE, emailCode);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070065 // send out the email
66 sendEmail(emailAddress, emailCode, request);
Zhiyi Zhang8fdb36b2020-10-18 11:58:51 -070067 NDN_LOG_TRACE("Secret for request " << toHex(request.m_requestId.data(), request.m_requestId.size()) << " : " << emailCode);
Zhiyi Zhanga749f442020-09-29 17:19:51 -070068 return returnWithNewChallengeStatus(request, NEED_CODE, std::move(secretJson), m_maxAttemptTimes, m_secretLifetime);
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080069 }
Zhiyi Zhanga749f442020-09-29 17:19:51 -070070 if (request.m_challengeState) {
71 if (request.m_challengeState->m_challengeStatus == NEED_CODE ||
72 request.m_challengeState->m_challengeStatus == WRONG_CODE) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -070073 NDN_LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeState->m_challengeStatus);
Zhiyi Zhanga749f442020-09-29 17:19:51 -070074 // the incoming interest should bring the pin code
tylerliu50d679e2020-10-14 14:08:39 -070075 std::string givenCode = readString(params.get(tlv::ParameterValue));
Zhiyi Zhanga749f442020-09-29 17:19:51 -070076 auto secret = request.m_challengeState->m_secrets;
77 // check if run out of time
78 if (currentTime - request.m_challengeState->m_timestamp >= m_secretLifetime) {
79 return returnWithError(request, ErrorCode::OUT_OF_TIME, "Secret expired.");
80 }
81 // check if provided secret is correct
82 if (givenCode == secret.get<std::string>(PARAMETER_KEY_CODE)) {
83 // the code is correct
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -070084 NDN_LOG_TRACE("Correct secret code. Challenge succeeded.");
Zhiyi Zhanga749f442020-09-29 17:19:51 -070085 return returnWithSuccess(request);
86 }
87 // otherwise, check remaining attempt times
88 if (request.m_challengeState->m_remainingTries > 1) {
89 auto remainTime = m_secretLifetime - (currentTime - request.m_challengeState->m_timestamp);
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -070090 NDN_LOG_TRACE("Wrong secret code provided. Remaining Tries - 1.");
Zhiyi Zhanga749f442020-09-29 17:19:51 -070091 return returnWithNewChallengeStatus(request, WRONG_CODE, std::move(secret),
92 request.m_challengeState->m_remainingTries - 1,
93 time::duration_cast<time::seconds>(remainTime));
94 }
95 else {
96 // run out times
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -070097 NDN_LOG_TRACE("Wrong secret code provided. Ran out tires. Challenge failed.");
Zhiyi Zhanga749f442020-09-29 17:19:51 -070098 return returnWithError(request, ErrorCode::OUT_OF_TRIES, "Ran out tires.");
99 }
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800100 }
101 }
Zhiyi Zhangaafc55e2020-09-28 17:54:48 -0700102 return returnWithError(request, ErrorCode::INVALID_PARAMETER, "Unexpected status or challenge status");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700103}
104
105// For Client
tylerliu40226332020-11-11 15:37:16 -0800106std::multimap<std::string, std::string>
Zhiyi Zhang46049832020-09-28 17:08:12 -0700107ChallengeEmail::getRequestedParameterList(Status status, const std::string& challengeStatus)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700108{
tylerliu40226332020-11-11 15:37:16 -0800109 std::multimap<std::string, std::string> result;
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700110 if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
tylerliu40226332020-11-11 15:37:16 -0800111 result.emplace(PARAMETER_KEY_EMAIL, "Please input your email address");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700112 }
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700113 else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
tylerliu40226332020-11-11 15:37:16 -0800114 result.emplace(PARAMETER_KEY_CODE, "Please input your verification code");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700115 }
Zhiyi Zhang48f23782020-09-28 12:11:24 -0700116 else if (status == Status::CHALLENGE && challengeStatus == WRONG_CODE) {
tylerliu40226332020-11-11 15:37:16 -0800117 result.emplace(PARAMETER_KEY_CODE, "Incorrect code, please try again");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700118 }
119 else {
tylerliu41c11532020-10-10 16:14:45 -0700120 NDN_THROW(std::runtime_error("Unexpected status or challenge status."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700121 }
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800122 return result;
123}
124
Suyong Won19fba4d2020-05-09 13:39:46 -0700125Block
Zhiyi Zhangba8e45e2020-11-03 08:55:32 -0800126ChallengeEmail::genChallengeRequestTLV(Status status, const std::string& challengeStatus,
tylerliu40226332020-11-11 15:37:16 -0800127 std::multimap<std::string, std::string>&& params)
Suyong Won19fba4d2020-05-09 13:39:46 -0700128{
tylerliu1f480be2020-11-10 13:02:53 -0800129 Block request(tlv::EncryptedPayload);
Zhiyi Zhang46049832020-09-28 17:08:12 -0700130 if (status == Status::BEFORE_CHALLENGE) {
tylerliu40226332020-11-11 15:37:16 -0800131 if (params.size() != 1 || params.find(PARAMETER_KEY_EMAIL) == params.end()) {
tylerliu41c11532020-10-10 16:14:45 -0700132 NDN_THROW(std::runtime_error("Wrong parameter provided."));
Zhiyi Zhang46049832020-09-28 17:08:12 -0700133 }
tylerliu50d679e2020-10-14 14:08:39 -0700134 request.push_back(makeStringBlock(tlv::SelectedChallenge, CHALLENGE_TYPE));
135 request.push_back(makeStringBlock(tlv::ParameterKey, PARAMETER_KEY_EMAIL));
tylerliu40226332020-11-11 15:37:16 -0800136 request.push_back(makeStringBlock(tlv::ParameterValue, params.find(PARAMETER_KEY_EMAIL)->second));
Suyong Won19fba4d2020-05-09 13:39:46 -0700137 }
Zhiyi Zhang46049832020-09-28 17:08:12 -0700138 else if (status == Status::CHALLENGE && (challengeStatus == NEED_CODE || challengeStatus == WRONG_CODE)) {
tylerliu40226332020-11-11 15:37:16 -0800139 if (params.size() != 1 || params.find(PARAMETER_KEY_CODE) == params.end()) {
tylerliu41c11532020-10-10 16:14:45 -0700140 NDN_THROW(std::runtime_error("Wrong parameter provided."));
Zhiyi Zhang46049832020-09-28 17:08:12 -0700141 }
tylerliu50d679e2020-10-14 14:08:39 -0700142 request.push_back(makeStringBlock(tlv::SelectedChallenge, CHALLENGE_TYPE));
143 request.push_back(makeStringBlock(tlv::ParameterKey, PARAMETER_KEY_CODE));
tylerliu40226332020-11-11 15:37:16 -0800144 request.push_back(makeStringBlock(tlv::ParameterValue, params.find(PARAMETER_KEY_CODE)->second));
Suyong Won19fba4d2020-05-09 13:39:46 -0700145 }
146 else {
tylerliu41c11532020-10-10 16:14:45 -0700147 NDN_THROW(std::runtime_error("Unexpected status or challenge status."));
Suyong Won19fba4d2020-05-09 13:39:46 -0700148 }
Suyong Won44d0cce2020-05-10 04:07:43 -0700149 request.encode();
Suyong Won19fba4d2020-05-09 13:39:46 -0700150 return request;
151}
152
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800153bool
154ChallengeEmail::isValidEmailAddress(const std::string& emailAddress)
155{
Zhiyi Zhang8ce677b2018-07-13 14:44:06 -0700156 const std::string pattern = R"_REGEX_((^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9\-\.]+$))_REGEX_";
157 static const std::regex emailPattern(pattern);
158 return std::regex_match(emailAddress, emailPattern);
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800159}
160
161void
Zhiyi Zhang576aad12017-10-03 15:41:53 -0700162ChallengeEmail::sendEmail(const std::string& emailAddress, const std::string& secret,
Zhiyi Zhang32d4b4e2020-10-28 22:10:49 -0700163 const ca::RequestState& request) const
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800164{
Zhiyi Zhang576aad12017-10-03 15:41:53 -0700165 std::string command = m_sendEmailScript;
Zhiyi Zhangba8e45e2020-11-03 08:55:32 -0800166 command += " \"" + emailAddress + "\" \"" + secret + "\" \"" +
167 request.m_caPrefix.toUri() + "\" \"" +
168 request.m_cert.getName().toUri() + "\"";
tylerliu1f480be2020-11-10 13:02:53 -0800169 boost::process::child child(command);
170 child.wait();
171 if (child.exit_code() != 0) {
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700172 NDN_LOG_TRACE("EmailSending Script " + m_sendEmailScript + " fails.");
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800173 }
Zhiyi Zhangd61b4a82020-10-10 15:18:43 -0700174 NDN_LOG_TRACE("EmailSending Script " + m_sendEmailScript +
tylerliu1f480be2020-11-10 13:02:53 -0800175 " was executed successfully with return value 0.");
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800176}
177
Zhiyi Zhange4891b72020-10-10 15:11:57 -0700178} // namespace ndncert
179} // namespace ndn