0 /* See LICENSE file for copyright and license details. */

1 #ifdef __linux__

2 #define OPENSSL_API_COMPAT 0x10101000L

3 #endif

4 #include <openssl/bn.h>

5 #include <openssl/evp.h>

6 #include <openssl/rsa.h>

7 #include <stdio.h>

8 #include <tls.h>

9 #include <openssl/x509.h>

10 #include <openssl/pem.h>

11 #include <openssl/err.h>

12 #include <string.h>

13 #include <pwd.h>

14 #include <unistd.h>

15 #include <sys/stat.h>

16 #ifdef __linux__

17 #include <sys/random.h>

18 #include <bsd/string.h>

19 #endif

20 #include "gemini.h"

21

22 char homefolder[1024];

23 int homepath_cached = 0;

24 int gethomefolder(char* path, size_t len) {

25 if (homepath_cached)

26 return strlcpy(path, homefolder, len);

27 struct passwd *pw = getpwuid(geteuid());

28 if (!pw) return 0;

29 size_t length = strlcpy(path, pw->pw_dir, len);

30 if (length >= len) return -1;

31 homepath_cached = 1;

32 strlcpy(homefolder, path, sizeof(homefolder));

33 return length;

34 }

35

36 char downloadfolder[1024];

37 int downloadpath_cached = 0;

38 int getdownloadfolder(char* path, size_t len) {

39 if (downloadpath_cached)

40 return strlcpy(path, downloadfolder, len);

41 size_t length = 0;

42 if (!homepath_cached)

43 length = gethomefolder(downloadfolder, sizeof(downloadfolder));

44 else

45 length = strlcpy(downloadfolder, homefolder, sizeof(downloadfolder));

46 length += strlcpy(&downloadfolder[length],

47 "/Downloads", sizeof(downloadfolder) - length);

48 struct stat _stat;

49 if (stat(downloadfolder, &_stat) && mkdir(downloadfolder, 0700))

50 return -1;

51 downloadpath_cached = 1;

52 strlcpy(path, downloadfolder, len);

53 return length;

54 }

55

56 char cachefolder[1024];

57 int cachepath_cached = 0;

58 int getcachefolder(char* path, size_t len) {

59 if (cachepath_cached)

60 return strlcpy(path, cachefolder, len);

61 int ret = gethomefolder(path, len);

62 if (ret == -1) return -1;

63 size_t length = ret;

64 if (length >= len) return -1;

65 length += strlcpy(&path[length], "/.cache/vgmi", len - length);

66 if (length >= len) return -1;

67 struct stat _stat;

68 if (stat(path, &_stat) && mkdir(path, 0700)) return -1;

69 cachepath_cached = 1;

70 strlcpy(cachefolder, path, sizeof(cachefolder));

71 return length;

72 }

73

74 int cert_getpath(char* host, char* crt, size_t crt_len, char* key, size_t key_len) {

75 char path[1024];

76 int ret = getcachefolder(path, sizeof(path));

77 if (ret < 1) {

78 snprintf(client.tabs[client.tab].error,

79 sizeof(client.tabs[client.tab].error),

80 "Failed to get cache directory");

81 return -1;

82 }

83 size_t len = ret;

84 if (len+1 >= sizeof(path))

85 goto getpath_overflow;

86 path[len] = '/';

87 path[++len] = '\0';

88 len += strlcpy(&path[len], host, sizeof(path) - len);

89 if (len >= sizeof(path))

90 goto getpath_overflow;

91 if (strlcpy(crt, path, crt_len) >= crt_len)

92 goto getpath_overflow;

93 if (strlcpy(key, path, key_len) >= key_len)

94 goto getpath_overflow;

95 if (crt_len - len < 4 || key_len - len < 4)

96 goto getpath_overflow;

97 if (strlcpy(&crt[len], ".crt", crt_len - len) + len >= crt_len)

98 goto getpath_overflow;

99 if (strlcpy(&key[len], ".key", key_len - len) + len >= key_len)

100 goto getpath_overflow;

101 return len+4;

102 getpath_overflow:

103 snprintf(client.tabs[client.tab].error,

104 sizeof(client.tabs[client.tab].error),

105 "The cache folder path is too long %!s(MISSING)", path);

106 return -1;

107 }

108

109 int cert_create(char* host) {

110 FILE *f = NULL;

111 int ret = 1;

112 EVP_PKEY* pkey;

113 pkey = EVP_PKEY_new();

114 RSA* rsa = RSA_new();

115 BIGNUM* bne = BN_new();

116 X509* x509 = X509_new();

117 if (BN_set_word(bne, 65537) != 1) goto failed;

118 if (RSA_generate_key_ex(rsa, 2048, bne, NULL) != 1) goto failed;

119

120 EVP_PKEY_assign_RSA(pkey, rsa);

121 int id;

122 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)

123 arc4random_buf(&id, sizeof(id));

124 #else

125 getrandom(&id, sizeof(id), GRND_RANDOM);

126 #endif

127 if (ASN1_INTEGER_set(X509_get_serialNumber(x509), id) != 1)

128 goto failed;

129

130 X509_gmtime_adj(X509_getm_notBefore(x509), 0);

131 X509_gmtime_adj(X509_getm_notAfter(x509), 157680000L);

132

133 if (X509_set_pubkey(x509, pkey) != 1) goto failed;

134

135 X509_NAME* name = X509_get_subject_name(x509);

136 if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,

137 (unsigned char *)host, -1, -1, 0) != 1)

138 goto failed;

139

140 if (X509_set_issuer_name(x509, name) != 1) goto failed;

141 if (X509_sign(x509, pkey, EVP_sha1()) == 0) goto failed;

142

143 char key[1024];

144 char crt[1024];

145 if (cert_getpath(host, crt, sizeof(crt), key, sizeof(key)) == -1)

146 goto skip_error;

147

148 f = fopen(key, "wb");

149 if (!f) {

150 snprintf(client.tabs[client.tab].error,

151 sizeof(client.tabs[client.tab].error),

152 "Failed to write to %!s(MISSING)", key);

153 goto skip_error;

154 }

155 if (PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL) != 1)

156 goto failed;

157 fclose(f);

158

159 f = fopen(crt, "wb");

160 if (!f) {

161 snprintf(client.tabs[client.tab].error,

162 sizeof(client.tabs[client.tab].error),

163 "Failed to write to %!s(MISSING)", crt);

164 goto skip_error;

165 }

166 if (PEM_write_X509(f, x509) != 1)

167 goto failed;

168 fclose(f);

169 f = NULL;

170 ret = 0;

171 goto skip_error;

172 failed:

173 snprintf(client.tabs[client.tab].error,

174 sizeof(client.tabs[client.tab].error),

175 "Failed to generate certificate");

176 skip_error:

177 if (f) fclose(f);

178 BN_free(bne);

179 EVP_PKEY_free(pkey);

180 X509_free(x509);

181 //RSA_free(rsa);

182 if (ret) client.input.error = 1;

183 return ret;

184 }

185