blob: dbd3b2c3948d0033205ee7eddd33269dc7206646 [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"
22#include "../logging.hpp"
Zhiyi Zhang8ce677b2018-07-13 14:44:06 -070023#include <regex>
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080024
25namespace ndn {
26namespace ndncert {
27
28_LOG_INIT(ndncert.ChallengeEmail);
29
30NDNCERT_REGISTER_CHALLENGE(ChallengeEmail, "Email");
31
32const std::string ChallengeEmail::NEED_CODE = "need-code";
33const std::string ChallengeEmail::WRONG_CODE = "wrong-code";
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080034const std::string ChallengeEmail::FAILURE_INVALID_EMAIL = "failure-invalid-email";
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080035const std::string ChallengeEmail::JSON_EMAIL = "email";
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080036const std::string ChallengeEmail::JSON_CODE = "code";
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080037
38ChallengeEmail::ChallengeEmail(const std::string& scriptPath,
39 const size_t& maxAttemptTimes,
40 const time::seconds secretLifetime)
Zhiyi Zhanga9bda732017-05-20 22:58:55 -070041 : ChallengeModule("Email")
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080042 , m_sendEmailScript(scriptPath)
43 , m_maxAttemptTimes(maxAttemptTimes)
44 , m_secretLifetime(secretLifetime)
45{
46}
47
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070048// For CA
49void
50ChallengeEmail::handleChallengeRequest(const JsonSection& params, CertificateRequest& request)
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080051{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070052 if (request.m_challengeStatus == "") {
53 // for the first time, init the challenge
54 std::string emailAddress = params.get<std::string>(JSON_EMAIL);
55 if (!isValidEmailAddress(emailAddress)) {
56 request.m_status = STATUS_FAILURE;
57 request.m_challengeStatus = FAILURE_INVALID_EMAIL;
58 return;
59 }
60 request.m_status = STATUS_CHALLENGE;
61 request.m_challengeStatus = NEED_CODE;
62 request.m_challengeType = CHALLENGE_TYPE;
63 std::string emailCode = generateSecretCode();
64 JsonSection secretJson;
65 secretJson.add(JSON_CODE, emailCode);
66 request.m_challengeSecrets = secretJson;
67 request.m_challengeTp = time::toIsoString(time::system_clock::now());
68 request.m_remainingTime = m_secretLifetime.count();
69 request.m_remainingTries = m_maxAttemptTimes;
70 // send out the email
71 sendEmail(emailAddress, emailCode, request);
72 _LOG_TRACE("Secret for request " << request.m_requestId << " : " << emailCode);
73 return;
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080074 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070075 else if (request.m_challengeStatus == NEED_CODE || request.m_challengeStatus == WRONG_CODE) {
76 _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeStatus);
77 // the incoming interest should bring the pin code
78 std::string givenCode = params.get<std::string>(JSON_CODE);
79 const auto realCode = request.m_challengeSecrets.get<std::string>(JSON_CODE);
80 if (time::system_clock::now() - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
81 // secret expires
82 request.m_status = STATUS_FAILURE;
83 request.m_challengeStatus = CHALLENGE_STATUS_FAILURE_TIMEOUT;
84 updateRequestOnChallengeEnd(request);
85 _LOG_TRACE("Secret expired. Challenge failed.");
86 return;
87 }
88 else if (givenCode == realCode) {
89 // the code is correct
90 request.m_status = STATUS_PENDING;
91 request.m_challengeStatus = CHALLENGE_STATUS_SUCCESS;
92 updateRequestOnChallengeEnd(request);
93 _LOG_TRACE("Secret code matched. Challenge succeeded.");
94 return;
Zhiyi Zhangdefa9592017-02-21 10:56:22 -080095 }
96 else {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070097 // check rest attempt times
98 if (request.m_remainingTries > 1) {
99 request.m_challengeStatus = WRONG_CODE;
100 request.m_remainingTries = request.m_remainingTries - 1;
101 auto remainTime = m_secretLifetime - (time::system_clock::now() - time::fromIsoString(request.m_challengeTp));
102 request.m_remainingTime = remainTime.count();
103 _LOG_TRACE("Secret code didn't match. Remaining Tries - 1.");
104 return;
105 }
106 else {
107 // run out times
108 request.m_status = STATUS_FAILURE;
109 request.m_challengeStatus = CHALLENGE_STATUS_FAILURE_MAXRETRY;
110 updateRequestOnChallengeEnd(request);
111 _LOG_TRACE("Secret code didn't match. Ran out tires. Challenge failed.");
112 return;
113 }
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800114 }
115 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700116 else {
117 _LOG_ERROR("The challenge status is wrong");
118 request.m_status = STATUS_FAILURE;
119 return;
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800120 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700121}
122
123// For Client
124JsonSection
125ChallengeEmail::getRequirementForChallenge(int status, const std::string& challengeStatus)
126{
127 JsonSection result;
128 if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
129 result.put(JSON_EMAIL, "Please_input_your_email_address");
130 }
131 else if (status == STATUS_CHALLENGE && challengeStatus == NEED_CODE) {
132 result.put(JSON_CODE, "Please_input_your_verification_code");
133 }
134 else if (status == STATUS_CHALLENGE && challengeStatus == WRONG_CODE) {
135 result.put(JSON_CODE, "Incorrect_code_please_try_again");
136 }
137 else {
138 _LOG_ERROR("CA's status and challenge status are wrong");
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800139 }
140 return result;
141}
142
143JsonSection
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700144ChallengeEmail::genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params)
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800145{
146 JsonSection result;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700147 if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
148 result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
149 result.put(JSON_EMAIL, params.get<std::string>(JSON_EMAIL, ""));
150 }
151 else if (status == STATUS_CHALLENGE && challengeStatus == NEED_CODE) {
152 result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
153 result.put(JSON_CODE, params.get<std::string>(JSON_CODE, ""));
154 }
155 else if (status == STATUS_CHALLENGE && challengeStatus == WRONG_CODE) {
156 result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
157 result.put(JSON_CODE, params.get<std::string>(JSON_CODE, ""));
158 }
159 else {
160 _LOG_ERROR("Client's status and challenge status are wrong");
161 }
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800162 return result;
163}
164
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800165bool
166ChallengeEmail::isValidEmailAddress(const std::string& emailAddress)
167{
Zhiyi Zhang8ce677b2018-07-13 14:44:06 -0700168 const std::string pattern = R"_REGEX_((^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9\-\.]+$))_REGEX_";
169 static const std::regex emailPattern(pattern);
170 return std::regex_match(emailAddress, emailPattern);
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800171}
172
173void
Zhiyi Zhang576aad12017-10-03 15:41:53 -0700174ChallengeEmail::sendEmail(const std::string& emailAddress, const std::string& secret,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700175 const CertificateRequest& request) const
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800176{
Zhiyi Zhang576aad12017-10-03 15:41:53 -0700177 std::string command = m_sendEmailScript;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700178 command += " \"" + emailAddress + "\" \"" + secret + "\" \"" + request.m_caName.toUri() + "\"";
Zhiyi Zhang576aad12017-10-03 15:41:53 -0700179 int result = system(command.c_str());
180 if (result == -1) {
181 _LOG_TRACE("EmailSending Script " + m_sendEmailScript + " fails.");
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800182 }
Zhiyi Zhang576aad12017-10-03 15:41:53 -0700183 _LOG_TRACE("EmailSending Script " + m_sendEmailScript +
184 " was executed successfully with return value" + std::to_string(result) + ".");
Zhiyi Zhangdefa9592017-02-21 10:56:22 -0800185 return;
186}
187
188} // namespace ndncert
189} // namespace ndn