1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2018 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 
24 #include "auth_pam.h"
25 
26 #include <security/pam_appl.h>
27 #include <readline/readline.h>
28 #include <readline/history.h>
29 
30 #include "include/bareos.h"
31 #include "console_output.h"
32 
33 enum class PamAuthState {
34    INIT,
35    RECEIVE_MSG_TYPE,
36    RECEIVE_MSG,
37    READ_INPUT,
38    SEND_INPUT,
39    AUTH_OK
40 };
41 
42 static PamAuthState state = PamAuthState::INIT;
43 static bool SetEcho(FILE *std_in, bool on);
44 
45 
ConsolePamAuthenticate(FILE * std_in,BareosSocket * UA_sock)46 bool ConsolePamAuthenticate(FILE *std_in, BareosSocket *UA_sock)
47 {
48    bool quit = false;
49    bool error = false;
50 
51    int type;
52    btimer_t *tid = nullptr;
53    char *userinput = nullptr;
54 
55    while (!error && !quit) {
56       switch(state) {
57          case PamAuthState::INIT:
58             if(tid) {
59                StopBsockTimer(tid);
60             }
61             tid = StartBsockTimer(UA_sock, 30);
62             if (!tid) {
63                error = true;
64             }
65             state = PamAuthState::RECEIVE_MSG_TYPE;
66             break;
67          case PamAuthState::RECEIVE_MSG_TYPE:
68             if (UA_sock->recv() == 1) {
69                type = UA_sock->msg[0];
70                switch (type) {
71                   case PAM_PROMPT_ECHO_OFF:
72                   case PAM_PROMPT_ECHO_ON:
73                      SetEcho (std_in, type == PAM_PROMPT_ECHO_ON);
74                      state = PamAuthState::RECEIVE_MSG;
75                      break;
76                   case PAM_SUCCESS:
77                      if (UA_sock->recv() == 1) {
78                         state = PamAuthState::AUTH_OK;
79                         quit = true;
80                      } else {
81                         Dmsg0(100, "Error, did not receive 2nd part of a message\n");
82                         error = true;
83                      }
84                      break;
85                   default:
86                      Dmsg1(100, "Error, unknown pam type %d\n", type);
87                      error = true;
88                      break;
89                } /* switch (type) */
90             } else {
91                error = true;
92             }
93             break;
94          case PamAuthState::RECEIVE_MSG:
95             if (UA_sock->recv() > 0) {
96                ConsoleOutput(UA_sock->msg);
97                state = PamAuthState::READ_INPUT;
98             } else {
99                error = true;
100             }
101             break;
102          case PamAuthState::READ_INPUT: {
103                userinput = readline("");
104                if (userinput) {
105                   state = PamAuthState::SEND_INPUT;
106                } else {
107                   error = true;
108                }
109             }
110             break;
111          case PamAuthState::SEND_INPUT:
112             UA_sock->fsend(userinput);
113             Actuallyfree(userinput);
114             userinput = nullptr;
115             state = PamAuthState::INIT;
116             break;
117          default:
118             break;
119       }
120       if (UA_sock->IsStop() || UA_sock->IsError()) {
121          if(userinput) {
122             Actuallyfree(userinput);
123             userinput = nullptr;
124          }
125          error = true;
126          break;
127       }
128    }; /* while (!quit) */
129 
130    if(tid) {
131       StopBsockTimer(tid);
132    }
133    SetEcho (std_in, true);
134    ConsoleOutput("\n");
135 
136    return !error;
137 }
138 
139 #include <termios.h>
SetEcho(FILE * std_in,bool on)140 static bool SetEcho(FILE *std_in, bool on)
141 {
142     struct termios termios_old, termios_new;
143 
144     /* Turn echoing off and fail if we can’t. */
145     if (tcgetattr (fileno (std_in), &termios_old) != 0)
146         return false;
147     termios_new = termios_old;
148     if (on) {
149       termios_new.c_lflag |=  ECHO;
150     } else {
151       termios_new.c_lflag &= ~ECHO;
152     }
153     if (tcsetattr (fileno (std_in), TCSAFLUSH, &termios_new) != 0)
154         return false;
155    return true;
156 }
157