I am trying to make mounting several Veracrypt volumes with the same password more convenient on the Linux commandline. Since Veracrypt does only support passphrase caching in GUI-mode, I wrote the following code to do the job for me:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<time.h>
int main(int argc, char* argv[]){
if(argc > 1 && argc%2==1){
srand(time(0));
//prevent veracrypt from asking for user's passphrase
system("sudo echo -n");
char *buffer = getpass("Veracrypt Password:");
for(int i = 1; i<argc; i+=2){
char* cc;
cc = (char *) malloc(57+strlen(argv[i])+1+strlen(argv[i+1]));
strcpy(cc, "veracrypt -t --protect-hidden=no --keyfiles=\"\" --pim=123 ");
strcat(cc, argv[i]);
strcat(cc, " ");
strcat(cc, argv[i+1]);
FILE* fChild = popen(cc, "w");
fprintf(fChild, "%s", buffer);
pclose(fChild);
free(cc);
}
for(int i = 0; i<strlen(buffer); i++)
buffer[i] = rand();
}
return 0;
}
The code works, but I wonder whether the passphrase is properly deleted from the memory after excecution. As shown in the code above, the passphrase is read into the char-array buffer in the beginning and replaced with random values in the end.
My two questions are:
Is this approach a good idea at all? (securitywise)
How is the value of buffer piped to veracrypt by popen()? / Is buffer read directly from its location or is it copied and can therefore remain somewhere in memory?
The approach is ok and is as secure as is pipeing the password in the shell
<<<"myPassword" veracrypt
.ps
output.Your code is not secure at all.
cc
. You didn't allocate place for the terminating null character. You could useasnprintf
and let GNU library do the job for you.argv[i]
andargv[i+1]
it's plain simple to attack any PC using your program, with just ex.:./your_program "; sudo rm -rf <some_file>" "; echo I can run any shell script here"
.The approach is ok, how you approached it is not ok. Your program leaks memory and doesn't check any return values and has no control over the strings passed to
popen
, which is just unsecure. Usingsystem(sudo echo -n)
is also insecure. As for the approuch, would be best to bzero the buffer after it's last usememset(buffer, 0, strlen(buffer) + 1)
(maybe multiple times, like 5), and thenfree(buffer)
.In the light of last attacks like Meltdown and Spectre and others, newer ssh versions encrypt the password with a long key (I think with RSA, not sure) right after receiving it from user and decrypt each time upon use. The key is long enough to make attacks using such methods not probable or too long. I don't think there is need for easy small application to implement such method. source.
Because you use
fprintf
, the buffer is copied into the internalFILE*
buffer and then flushed on the newline. By defaultFILE*
streams are buffered and flushed on newline. You can specify the behavior withsetvbuf
, however, I don't think it's safe at all, as the password will remain in theFILE*
buffer for some time. Then thefprintf
call writes the content of the internalFILE*
buffer upon newline to the associated pipe file descriptor with theFILE*
pointer. Then kernel passes the data from the pipe's input to the command's stdin. A little tiny bit safer way (as you don't needprintf
utility at all, you just"%s"
...), is probably to usesetvbuf(fChild, NULL, _IONBF, 0)
and then to usefwrite(buffer, strlen(buffer), 1, fChild)
.A proper approach would be to remove the
FILE*
and to use the properpipe()
+fork()
+exec()
and stream the password directly into the pipe withwrite()
call, so you don't useFILE*
internal buffering.fork()
will also allow you to send signals and handle the return value of the child.Yes and yes and yes. It is read directly from it's location inside
fprintf
call. It is copied into internalFILE*
buffer. It can therefore remain somewhere in memory.