Coverage Report

Created: 2023-09-25 07:17

/src/neomutt/conn/connaccount.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Connection Credentials
4
 *
5
 * @authors
6
 * Copyright (C) 2020 Richard Russon <rich@flatcap.org>
7
 *
8
 * @copyright
9
 * This program is free software: you can redistribute it and/or modify it under
10
 * the terms of the GNU General Public License as published by the Free Software
11
 * Foundation, either version 2 of the License, or (at your option) any later
12
 * version.
13
 *
14
 * This program is distributed in the hope that it will be useful, but WITHOUT
15
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17
 * details.
18
 *
19
 * You should have received a copy of the GNU General Public License along with
20
 * this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/**
24
 * @page conn_account Connection Credentials
25
 *
26
 * Connection Credentials
27
 */
28
29
#include "config.h"
30
#include <assert.h>
31
#include <stdbool.h>
32
#include <stdio.h>
33
#include <string.h>
34
#include <sys/types.h>
35
#include "mutt/lib.h"
36
#include "gui/lib.h"
37
#include "mutt.h"
38
#include "connaccount.h"
39
#include "editor/lib.h"
40
#include "history/lib.h"
41
#include "accountcmd.h"
42
#include "globals.h"
43
44
/**
45
 * mutt_account_getuser - Retrieve username into ConnAccount, if necessary
46
 * @param cac ConnAccount to fill
47
 * @retval  0 Success
48
 * @retval -1 Failure
49
 */
50
int mutt_account_getuser(struct ConnAccount *cac)
51
0
{
52
0
  if (cac->flags & MUTT_ACCT_USER)
53
0
    return 0;
54
0
  if (!cac->get_field)
55
0
    return -1;
56
57
0
  const char *user = cac->get_field(MUTT_CA_USER, cac->gf_data);
58
0
  if (user)
59
0
  {
60
0
    mutt_str_copy(cac->user, user, sizeof(cac->user));
61
0
  }
62
0
  else if (mutt_account_call_external_cmd(cac) != MUTT_ACCT_NO_FLAGS)
63
0
  {
64
    /* The external command might interact with the screen */
65
0
    if (!OptNoCurses)
66
0
      mutt_need_hard_redraw();
67
0
    return 0;
68
0
  }
69
0
  else if (OptNoCurses)
70
0
  {
71
0
    return -1;
72
0
  }
73
0
  else
74
0
  {
75
    /* prompt (defaults to unix username), copy into cac->user */
76
0
    char prompt[256] = { 0 };
77
    /* L10N: Example: Username at myhost.com */
78
0
    snprintf(prompt, sizeof(prompt), _("Username at %s: "), cac->host);
79
0
    mutt_str_copy(cac->user, Username, sizeof(cac->user));
80
81
0
    struct Buffer *buf = buf_pool_get();
82
0
    const int rc = mw_get_field(prompt, buf, MUTT_COMP_UNBUFFERED, HC_OTHER, NULL, NULL);
83
0
    mutt_str_copy(cac->user, buf_string(buf), sizeof(cac->user));
84
0
    buf_pool_release(&buf);
85
0
    if (rc != 0)
86
0
      return -1;
87
0
  }
88
89
0
  cac->flags |= MUTT_ACCT_USER;
90
0
  return 0;
91
0
}
92
93
/**
94
 * mutt_account_getlogin - Retrieve login info into ConnAccount, if necessary
95
 * @param cac ConnAccount to fill
96
 * @retval  0 Success
97
 * @retval -1 Failure
98
 */
99
int mutt_account_getlogin(struct ConnAccount *cac)
100
0
{
101
0
  if (cac->flags & MUTT_ACCT_LOGIN)
102
0
    return 0;
103
0
  if (!cac->get_field)
104
0
    return -1;
105
106
0
  const char *login = cac->get_field(MUTT_CA_LOGIN, cac->gf_data);
107
0
  if (!login && (mutt_account_getuser(cac) == 0))
108
0
  {
109
0
    login = cac->user;
110
0
  }
111
112
0
  if (!login)
113
0
  {
114
0
    mutt_debug(LL_DEBUG1, "Couldn't get user info\n");
115
0
    return -1;
116
0
  }
117
118
0
  mutt_str_copy(cac->login, login, sizeof(cac->login));
119
0
  cac->flags |= MUTT_ACCT_LOGIN;
120
0
  return 0;
121
0
}
122
123
/**
124
 * mutt_account_getpass - Fetch password into ConnAccount, if necessary
125
 * @param cac ConnAccount to fill
126
 * @retval  0 Success
127
 * @retval -1 Failure
128
 */
129
int mutt_account_getpass(struct ConnAccount *cac)
130
0
{
131
0
  if (cac->flags & MUTT_ACCT_PASS)
132
0
    return 0;
133
0
  if (!cac->get_field)
134
0
    return -1;
135
136
0
  const char *pass = cac->get_field(MUTT_CA_PASS, cac->gf_data);
137
0
  if (pass)
138
0
  {
139
0
    mutt_str_copy(cac->pass, pass, sizeof(cac->pass));
140
0
  }
141
0
  else if (mutt_account_call_external_cmd(cac) != MUTT_ACCT_NO_FLAGS)
142
0
  {
143
    /* The external command might interact with the screen */
144
0
    if (!OptNoCurses)
145
0
      mutt_need_hard_redraw();
146
0
    return 0;
147
0
  }
148
0
  else if (OptNoCurses)
149
0
  {
150
0
    return -1;
151
0
  }
152
0
  else
153
0
  {
154
0
    char prompt[256] = { 0 };
155
0
    snprintf(prompt, sizeof(prompt), _("Password for %s@%s: "),
156
0
             (cac->flags & MUTT_ACCT_LOGIN) ? cac->login : cac->user, cac->host);
157
0
    cac->pass[0] = '\0';
158
159
0
    struct Buffer *buf = buf_pool_get();
160
0
    const int rc = mw_get_field(prompt, buf, MUTT_COMP_PASS | MUTT_COMP_UNBUFFERED,
161
0
                                HC_OTHER, NULL, NULL);
162
0
    mutt_str_copy(cac->pass, buf_string(buf), sizeof(cac->pass));
163
0
    buf_pool_release(&buf);
164
0
    if (rc != 0)
165
0
      return -1;
166
0
  }
167
168
0
  cac->flags |= MUTT_ACCT_PASS;
169
0
  return 0;
170
0
}
171
172
/**
173
 * mutt_account_unsetpass - Unset ConnAccount's password
174
 * @param cac ConnAccount to modify
175
 */
176
void mutt_account_unsetpass(struct ConnAccount *cac)
177
0
{
178
0
  cac->flags &= ~MUTT_ACCT_PASS;
179
0
  memset(cac->pass, 0, sizeof(cac->pass));
180
0
}
181
182
/**
183
 * mutt_account_getoauthbearer - Get an OAUTHBEARER/XOAUTH2 token
184
 * @param cac Account to use
185
 * @param xoauth2 Generate a deprecated XOAUTH2 token
186
 * @retval ptr  OAuth token
187
 * @retval NULL Error
188
 *
189
 * Run an external command to generate the oauth refresh token for an account,
190
 * then create and encode the OAUTHBEARER token based on RFC7628.
191
 *
192
 * @note Caller should free the token
193
 */
194
char *mutt_account_getoauthbearer(struct ConnAccount *cac, bool xoauth2)
195
0
{
196
0
  if (!cac || !cac->get_field)
197
0
    return NULL;
198
199
  /* The oauthbearer token includes the login */
200
0
  if (mutt_account_getlogin(cac))
201
0
    return NULL;
202
203
0
  const char *cmd = cac->get_field(MUTT_CA_OAUTH_CMD, cac->gf_data);
204
0
  if (!cmd)
205
0
  {
206
    /* L10N: You will see this error message if (1) you have "oauthbearer" in
207
       one of your $*_authenticators and (2) you do not have the corresponding
208
       $*_oauth_refresh_command defined. So the message does not mean "None of
209
       your $*_oauth_refresh_command's are defined." */
210
0
    mutt_error(_("No OAUTH refresh command defined"));
211
0
    return NULL;
212
0
  }
213
214
0
  FILE *fp = NULL;
215
0
  pid_t pid = filter_create(cmd, NULL, &fp, NULL, EnvList);
216
0
  if (pid < 0)
217
0
  {
218
0
    mutt_perror(_("Unable to run refresh command"));
219
0
    return NULL;
220
0
  }
221
222
0
  size_t token_size = 0;
223
0
  char *token = mutt_file_read_line(NULL, &token_size, fp, NULL, MUTT_RL_NO_FLAGS);
224
0
  mutt_file_fclose(&fp);
225
0
  filter_wait(pid);
226
227
  /* The refresh cmd in some cases will invoke gpg to decrypt a token */
228
0
  if (!OptNoCurses)
229
0
    mutt_need_hard_redraw();
230
231
0
  if (!token || (*token == '\0'))
232
0
  {
233
0
    mutt_error(_("Command returned empty string"));
234
0
    FREE(&token);
235
0
    return NULL;
236
0
  }
237
238
0
  if ((!xoauth2 && (token_size > 512)) || (xoauth2 && (token_size > 4096)))
239
0
  {
240
0
    mutt_error(_("OAUTH token is too big: %ld"), token_size);
241
0
    FREE(&token);
242
0
    return NULL;
243
0
  }
244
245
  /* 4500 is chosen to allow for both a token that is 4096-long plus a
246
   * username that can be up to 320-long. */
247
0
  char oauthbearer[4500] = { 0 };
248
0
  int oalen = 0;
249
0
  if (xoauth2)
250
0
  {
251
0
    oalen = snprintf(oauthbearer, sizeof(oauthbearer),
252
0
                     "user=%s\001auth=Bearer %s\001\001", cac->login, token);
253
0
  }
254
0
  else
255
0
  {
256
0
    oalen = snprintf(oauthbearer, sizeof(oauthbearer),
257
0
                     "n,a=%s,\001host=%s\001port=%d\001auth=Bearer %s\001\001",
258
0
                     cac->login, cac->host, cac->port, token);
259
0
  }
260
0
  FREE(&token);
261
262
0
  size_t encoded_len = oalen * 4 / 3 + 10;
263
0
  assert(encoded_len < 6010); // Assure LGTM that we won't overflow
264
265
0
  char *encoded_token = mutt_mem_malloc(encoded_len);
266
0
  mutt_b64_encode(oauthbearer, oalen, encoded_token, encoded_len);
267
268
0
  return encoded_token;
269
0
}