TJAlienCredentials.cxx 9.17 KB
Newer Older
Nikola Hardi's avatar
Nikola Hardi committed
1
#include "TJAlienCredentials.h"
2
#include <cerrno>
Nikola Hardi's avatar
Nikola Hardi committed
3
#include <cstdlib>
4
#include <fcntl.h>
Nikola Hardi's avatar
Nikola Hardi committed
5
#include <fstream>
6
#include <iostream>
7
8
9
10
#include <sstream>
#include <unistd.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
Nikola Hardi's avatar
Nikola Hardi committed
11

12
13
using std::endl;
using std::getenv;
Nikola Hardi's avatar
Nikola Hardi committed
14
using std::ifstream;
15
16
17
using std::ofstream;
using std::stringstream;

18
19
const char *TJAlienCredentials::ENV_JOBTOKEN_KEY = "JALIEN_TOKEN_KEY";
const char *TJAlienCredentials::ENV_JOBTOKEN_CERT = "JALIEN_TOKEN_CERT";
20

21
22
23
24
const char *TJAlienCredentials::TMP_JOBTOKEN_KEY_FNAME_PREFIX =
    "tmpjobtokenkey_";
const char *TJAlienCredentials::TMP_JOBTOKEN_CERT_FNAME_PREFIX =
    "tmpjobtokencert_";
25

26
27
28
bool fileExists(const string &filename) {
  bool fileExists = false;
  FILE *f = fopen(filename.c_str(), "r");
29

30
31
32
33
34
35
  if (f != NULL) {
    fclose(f);
    fileExists = true;
  } else {
    fileExists = false;
  }
36

37
  return fileExists;
38
39
40
}

bool TJAlienCredentialsObject::exists() {
41
  return fileExists(certpath) && fileExists(keypath);
42
43
}

44
45
46
47
48
49
50
51
52
53
void TJAlienCredentials::writeSafeFile(const string &filename,
                                       const string &content) {
  int gDebug = std::getenv("gDebug") ? std::stoi(std::getenv("gDebug")) : 0;
  if (gDebug)
    INFO("writing safe file " << filename.c_str());
  int fd = open(filename.c_str(), O_RDWR | O_CREAT, 0600);
  if (write(fd, content.c_str(), content.length()) == -1)
    if (gDebug)
      ERROR("writing safe file failed: " << std::strerror(errno));
  close(fd);
54
55
}

56
57
string TJAlienCredentials::getSafeFilename(const string &prefix) {
  string filename = TJAlienCredentials::getTmpDir() + "/" + prefix;
58

59
  const char *JOB_ID = getenv("ALIEN_PROC_ID");
60

61
62
63
64
65
  if (JOB_ID != NULL) {
    filename += JOB_ID;
  } else {
    pid_t pid = getpid();
    unsigned int rnd = random() % 100 + 1;
66

67
68
69
70
    do {
      filename += std::to_string(pid) + "_" + std::to_string(rnd);
    } while (fileExists(filename));
  }
71

72
  return filename;
73
}
Nikola Hardi's avatar
Nikola Hardi committed
74

75
76
std::string TJAlienCredentials::getTmpDir() {
  std::string tmpdir;
Nikola Hardi's avatar
Nikola Hardi committed
77

78
79
80
81
82
83
84
85
  if (getenv("TMPDIR") != NULL)
    tmpdir = getenv("TMPDIR");
  else if (getenv("TMP") != NULL)
    tmpdir = getenv("TMP");
  else if (getenv("TEMP") != NULL)
    tmpdir = getenv("TEMP");
  else
    tmpdir = P_tmpdir;
Nikola Hardi's avatar
Nikola Hardi committed
86

87
  return tmpdir;
Nikola Hardi's avatar
Nikola Hardi committed
88
89
}

90
91
std::string TJAlienCredentials::getHomeDir() {
  std::string homedir;
Nikola Hardi's avatar
Nikola Hardi committed
92

93
94
95
96
  if (getenv("HOME") != NULL)
    homedir = getenv("HOME");
  else
    homedir = "~";
Nikola Hardi's avatar
Nikola Hardi committed
97

98
  return homedir;
Nikola Hardi's avatar
Nikola Hardi committed
99
100
}

101
102
103
104
105
std::string TJAlienCredentials::getTokencertPath() {
  std::stringstream tokencert_s;
  tokencert_s << tmpdir << "/tokencert_" << getuid() << ".pem";
  std::string tokencert = tokencert_s.str();
  std::string tokencertpath = std::getenv("JALIEN_TOKEN_CERT") ?: tokencert;
Nikola Hardi's avatar
Nikola Hardi committed
106

107
  return tokencertpath;
Nikola Hardi's avatar
Nikola Hardi committed
108
109
}

110
111
112
113
114
std::string TJAlienCredentials::getTokenkeyPath() {
  std::stringstream tokenkey_s;
  tokenkey_s << tmpdir << "/tokenkey_" << getuid() << ".pem";
  std::string tokenkey = tokenkey_s.str();
  std::string tokenkeypath = std::getenv("JALIEN_TOKEN_KEY") ?: tokenkey;
Nikola Hardi's avatar
Nikola Hardi committed
115

116
  return tokenkeypath;
Nikola Hardi's avatar
Nikola Hardi committed
117
118
}

119
120
121
122
std::string TJAlienCredentials::getUsercertPath() {
  std::string usercert = homedir + "/.globus/usercert.pem";
  std::string usercertpath = std::getenv("X509_USER_CERT") ?: usercert;
  return usercertpath;
Nikola Hardi's avatar
Nikola Hardi committed
123
124
}

125
126
127
128
string TJAlienCredentials::getUserkeyPath() {
  std::string userkey = homedir + "/.globus/userkey.pem";
  std::string userkeypath = std::getenv("X509_USER_KEY") ?: userkey;
  return userkeypath;
Nikola Hardi's avatar
Nikola Hardi committed
129
130
131
}

TJAlienCredentials::TJAlienCredentials() {
132
133
  tmpdir = getTmpDir();
  homedir = getHomeDir();
Nikola Hardi's avatar
Nikola Hardi committed
134
135
136
}

void TJAlienCredentials::loadCredentials() {
137
  removeCredentials(cJOB_TOKEN);
138
139
140
141
  found_credentials.clear();
  loadTokenCertificate();
  loadFullGridCertificate();
  loadJobTokenCertificate();
Nikola Hardi's avatar
Nikola Hardi committed
142
143
144
}

void TJAlienCredentials::loadTokenCertificate() {
145
146
  TJAlienCredentialsObject token_credentials(getTokencertPath(),
                                             getTokenkeyPath(), cJBOX_TOKEN);
Nikola Hardi's avatar
Nikola Hardi committed
147

148
149
150
  if (token_credentials.exists()) {
    found_credentials[cJBOX_TOKEN] = token_credentials;
  }
Nikola Hardi's avatar
Nikola Hardi committed
151
152
153
}

void TJAlienCredentials::loadFullGridCertificate() {
154
155
  TJAlienCredentialsObject grid_certificate(getUsercertPath(), getUserkeyPath(),
                                            cFULL_GRID_CERT);
Nikola Hardi's avatar
Nikola Hardi committed
156

157
158
159
  if (grid_certificate.exists()) {
    found_credentials[cFULL_GRID_CERT] = grid_certificate;
  }
Nikola Hardi's avatar
Nikola Hardi committed
160
161
}

162
void TJAlienCredentials::loadJobTokenCertificate() {
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  const char *env_cert = getenv(ENV_JOBTOKEN_CERT);
  const char *env_key = getenv(ENV_JOBTOKEN_KEY);

  // if it doesn't have both environment variables
  if (!env_cert || !env_key) {
    return;
  }

  // environment variables contain valid filepaths instead of the actual token
  if (fileExists(env_cert) && fileExists(env_key)) {
    found_credentials[cJOB_TOKEN] =
        TJAlienCredentialsObject(env_cert, env_key, cJOB_TOKEN);
  } else {
    const string &tmpcertpath = getSafeFilename(TMP_JOBTOKEN_CERT_FNAME_PREFIX);
    writeSafeFile(tmpcertpath, env_cert);

    const string &tmpkeypath = getSafeFilename(TMP_JOBTOKEN_KEY_FNAME_PREFIX);
    writeSafeFile(tmpkeypath, env_key);

    found_credentials[cJOB_TOKEN] =
        TJAlienCredentialsObject(tmpcertpath, tmpkeypath, cJOB_TOKEN, true);
  }
}
186

187
188
189
bool TJAlienCredentials::has(CredentialsKind kind) const {
  return found_credentials.count(kind) == 1;
}
190

191
192
193
194
195
196
197
TJAlienCredentialsObject TJAlienCredentials::get(CredentialsKind kind) const {
  if (this->has(kind)) {
    return found_credentials.at(kind);
  } else {
    return TJAlienCredentialsObject();
  }
}
198

199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
TJAlienCredentialsObject TJAlienCredentials::get() {
  if (this->has(cJOB_TOKEN)) {
    return this->get(cJOB_TOKEN);
  } else if (this->has(cJBOX_TOKEN)) {
    return this->get(cJBOX_TOKEN);
  } else if (this->has(cFULL_GRID_CERT)) {
    TJAlienCredentialsObject co = this->get(cFULL_GRID_CERT);
    if (co.password.empty())
      co.readPassword();
    return co;
  } else {
    ERROR("Failed to get any credentials");
    return TJAlienCredentialsObject();
  }
}

void TJAlienCredentials::removeCredentials(CredentialsKind kind) {
  if (this->has(kind)) {
    if (kind == cJOB_TOKEN)
      get(kind).wipe();

    found_credentials.erase(kind);
  }
}

short TJAlienCredentials::count() { return found_credentials.size(); }
225

226
227
228
229
230
231
232
233
std::string readFile(const char *filename) {
  std::string line;
  std::stringstream contents;
  std::ifstream f(filename);

  if (f.is_open()) {
    while (getline(f, line)) {
      contents << line << std::endl;
234
    }
235
236
237
238
239
240
  }
  return contents.str();
}

const std::string TJAlienCredentialsObject::getKey() {
  return readFile(keypath.c_str());
241
242
}

243
244
const std::string TJAlienCredentialsObject::getCertificate() {
  return readFile(certpath.c_str());
Nikola Hardi's avatar
Nikola Hardi committed
245
246
}

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
void TJAlienCredentialsObject::readPassword() {
  if (this->kind == cFULL_GRID_CERT &&
      this->getKey().find("ENCRYPTED") != std::string::npos) {
    printf("[Grid certificate password: ]");
    struct termios termold, termnew;
    tcgetattr(fileno(stdin), &termold);
    termnew = termold;
    termnew.c_lflag &= ~ECHO;
    termnew.c_lflag |= ECHONL;
    tcsetattr(fileno(stdin), TCSANOW, &termnew);

    char password[64];
    if (fgets(password, sizeof(password), stdin) != nullptr) {
      tcsetattr(0, TCSANOW, &termold);

      password[strlen(password) - 1] = 0;
      this->password = std::string(password);
264
    } else {
265
266
      ERROR("Error while reading from stdin");
      this->password = "";
Nikola Hardi's avatar
Nikola Hardi committed
267
    }
268
269
  } else
    this->password = "";
Nikola Hardi's avatar
Nikola Hardi committed
270
}
Nikola Hardi's avatar
Nikola Hardi committed
271

272
273
274
const std::string TJAlienCredentialsObject::getPassword() {
  if (this->password.empty())
    readPassword();
275

276
  return this->password;
277
278
}

279
280
TJAlienCredentials::~TJAlienCredentials() {
  removeCredentials(cJOB_TOKEN);
281
282
}

283
284
285
286
287
288
289
void TJAlienCredentials::selectPreferedCredentials() {
  msg = "";

  if(has(cJOB_TOKEN)) {
    preferedCredentials = cJOB_TOKEN;
    return;
  }
290

291
292
293
294
295
296
  if (has(cJBOX_TOKEN)) {
    if(!checkCertValidity(getTokencertPath().c_str())) {
      msg += "Token certificate is invalid or expired and it should be renewed.\n";
    } else {
      preferedCredentials = cJBOX_TOKEN;
      return;
297
    }
298
299
300
301
302
303
304
  }

  if (has(cFULL_GRID_CERT)) {
    msg += "Fallback to full grid cert - please use alien-token-init or TGrid::Connect() to renew it.\n";
    preferedCredentials = cFULL_GRID_CERT;
    return;
  }
305

306
307
  msg += "Failed to find any credentials\n";
  preferedCredentials = cNOT_FOUND;
Nikola Hardi's avatar
Nikola Hardi committed
308
309
}

310
311
CredentialsKind TJAlienCredentials::getPreferedCredentials() const {
  return preferedCredentials;
312
313
}

314
315
const string& TJAlienCredentials::getMessages() const {
  return msg;
316
317
}

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
bool TJAlienCredentials::checkCertValidity(const char *path) {
  FILE *fCert = fopen(path, "r");
  bool result = false;
  X509 *x509 = NULL;
  ASN1_TIME *expire = NULL;
  using namespace std;

  // failed to open certificate file
  if(!fCert) {
    return false;
  }

  // if file exists but expired,
  char cert[4096];
  fread(cert, 1, 4096, fCert);
  fclose(fCert);

  BIO *b = BIO_new(BIO_s_mem());
  BIO_puts(b, cert);
  x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);

  if(!x509) {
    result = false;
  } else {
    expire = X509_get_notAfter(x509);
  }

  // Failed to parse certificate expiration time
  if(expire != NULL) {
    if(!ASN1_TIME_check(expire)) {
      result = false;
    } else {
      int day, sec;
351

352
353
354
355
356
      // Compare certificate expiration time with current time
      ASN1_TIME_diff(&day, &sec, NULL, expire);
      result =  day > 0 || sec > 0;
    }
  }
357

358
359
360
361
  if(b) BIO_free(b);
  if(x509) X509_free(x509);

  return result;
Nikola Hardi's avatar
Nikola Hardi committed
362
}