libvirt_cloud/roles/toxcore/overlay/Linux/usr/local/src/gridfire/gridfire.py

1239 lines
46 KiB
Python
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/local/bin/python3.sh
# -*-mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
# https://github.com/reid-k/gridfire
from __future__ import unicode_literals, print_function
ROLE="proxy" # noqa
__program__ = "Gridfire"
__version__ = "v0.5+testforge.21.11"
__doc__ = """
Gridfire is inspired by gpggrid, a security tool included in
Tinfoil Hat Linux, intended to resist shoulder-surfing and keylogging.
For more information on the project, see the README file distributed with Gridfire.
Gridfire is named after a fictional superweapon in Iain M Banks' Culture novels.
See http://everything2.com/title/Gridfire for more.
RIP Iain M Banks (1954-02-16 to 2013-06-09)
usage: gridfire.py [-h] [-a TTYALERT] [-c] [-g] [-u] [-l] [-b] [-o] [-d] [-f]
[-p] [-w] [-m METHOD] [-n] [-v VERBOSITY]
[-S SINGLE] [-D DOUBLE] [-E --EQUAL] [-R --REPEAT ] [-B --BG ]
[-I --STDIN ] [ -P POS] [-H HEADER] [-A ANSWER] [-O fd]
[STDIN COMMAND [STDIN ARGS ...]]
positional arguments:
STDIN COMMAND Arguments to run the subprocess wih the password in stdin.
optional arguments:
-h, --help show this help message and exit
-a TTYALERT, --ttyalert TTYALERT
Set the alert mode (none, beep or flash)
-c Force concealment of entered text.
-g, --grid Ignore other options and behave like gpggrid.
-u Allow uppercase letters [A-Z].
-l Allow lowercase letters [a-z].
-b Allow binary numbers [0-1].
-o Allow octal numbers [0-7].
-d Allow decimal numbers [0-9].
-f Allow hexadecimal numbers [0-9][a-f][A-F].
-p Allow punctuation.
-w Allow whitespace.
-m METHOD, --method METHOD
Specify indexing method (0-3, higher is more secure,
default=3)
-n Append N newlines on output.
-v VERBOSITY, --verbosity VERBOSITY
Specify verbosity (0-5, higher is verbose, default=3)
-S SINGLE, --single SINGLE
Arg to pass to the subprocess with -SINGLE=password.
-D DOUBLE, --double DOUBLE
Arg to pass to the subprocess with --DOUBLE=password.
-E, --equal
Use = instead of space between single/double an arg.
-R, --repeat
Repeat the password with a newline to the subprocess.
-I, --stdin
Put the password on stdin to the subprocess.
-B, --bg
Put the subprocess in the bg.
-K descr, --session_add_key description
session_add_key 'some description'
-P POS, --pos POS Position to place the Arg to the subprocess with
--DOUBLE/SINGLE=password(default 0).
-H HEADER, --header HEADER
Header line as a prompt (default "").
-A ANSWER, --answer ANSWER
Skip the grid and give the answer (for testing only).
"""
#Learn more about Tinfoil Hat Linux:
#Homepage: http://tinfoilhat.shmoo.com/
#Wikipedia: https://en.wikipedia.org/wiki/Tinfoil_Hat_Linux
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#TODO: Decide whether or not implement method 4
#TODO: Fix row index for non-default charsets
import os
import sys
import curses
import locale
import string
import random
import argparse
import _curses
from io import StringIO
import shlex
import shutil
import subprocess
from time import sleep
import traceback
sDir = os.path.dirname(os.path.realpath(__file__))
sDir in sys.path or sys.path.append(sDir)
try:
from keepassxc_cmd2.cmd2_ansi import *
except Exception:
sys.stderr.write('\n'.join(sys.path))
def green(s): return s
def red(s): return s
def yellow(s): return s
def blue(s): return s
PY3 = sys.version_info[0] == 3
VERBOSITY = VERBOSE = 3
#
# 0: stdin
# 1: stdout
# 2: stderr
iOUT_FD = sys.stdout
try:
import sh
except ImportError:
sh = None
try:
import keyutils
except ImportError:
keyutils = None
try:
from pyassuan.bin.pinentry import PinEntry
except ImportError: PinEntry = None
try:
sPinentryCurses = shutil.which('pinentry-curses')
except Exception: sPinentryCurses = ''
try:
from dialog import Dialog
except ImportError: Dialog = None
oDIALOG = None
oLAST_DIR = '/tmp/'
# Allow stderr for the caller to use
def debug(message):
if VERBOSITY <= 4: return
sys.stdout.write('DEBUG: ' + repr(message) +'\n')
pass
def info(message):
if VERBOSITY <= 3: return
sys.stdout.write('INFO: ' + repr(message) +'\n')
pass
def warn(message):
if VERBOSITY <= 2: return
sys.stdout.write('WARN: ' + repr(message) +'\n')
pass
def error(message):
if VERBOSITY <= 1: return
sys.stdout.write('ERROR: ' + message +'\n')
pass
class DummyContextManager:
def __enter__(self):
return self
def __exit__(self, *exc):
return False
try:
devnull = subprocess.DEVNULL
except AttributeError: # Python < 3.3
devnull_context = devnull = open(os.devnull, "wb")
else:
devnull_context = DummyContextManager()
#Helps curses play nice
locale.setlocale(locale.LC_ALL, '')
code = locale.getpreferredencoding()
##### Defaults & Initial variables #####
charsets = ''
min_x, min_y = 55, 17
upperchar = ''
lowerchar = ''
out = ''
hide_out = 0
newline = 1
def spaceout(s):
i=len(s)-1 #number of spaces to be inserted
while i >= 1:
s = s[:-i]+" "+s[-i:]
i -= 1
return s
def make_parser():
"""Argument parsing"""
global VERBOSITY
parser = argparse.ArgumentParser(add_help=True)
parser.add_argument('-a', '--ttyalert', type=str, help='Set the alert mode (none, beep or flash)')
parser.add_argument('-c', help="Force concealment of entered text.", action="store_true")
parser.add_argument('-g', '--grid', help="Ignore other options and behave like gpggrid.", action="store_true")
#~ parser.add_argument('-u', dest='char_u', action='store_true')
parser.add_argument('-u', help="Allow uppercase letters [A-Z].", action="store_true")
parser.add_argument('-l', help="Allow lowercase letters [a-z].", action="store_true")
parser.add_argument('-b', help="Allow binary numbers [0-1].", action="store_true")
parser.add_argument('-o', help="Allow octal numbers [0-7].", action="store_true")
parser.add_argument('-d', help="Allow decimal numbers [0-9].", action="store_true")
parser.add_argument('-f', help="Allow hexadecimal numbers [0-9][a-f][A-F].", action="store_true")
parser.add_argument('-p', help="Allow punctuation.", action="store_true")
parser.add_argument('-w', help="Allow whitespace.", action="store_true")
parser.add_argument('-m', '--method', help='Specify indexing method (0-3, higher is more secure, default=3)', dest='method', type=int, default=3)
parser.add_argument('-n', help="Append newlines on output.", dest='newline', type=int, default=1)
parser.add_argument('-v', '--verbosity', help='Specify verbosity (0-5, higher is verbose, default=' +str(VERBOSITY) +')', type=int, default=VERBOSITY)
parser.add_argument('-S', '--single', help="Arg to pass to the subprocess with -SINGLE=password.", dest="single", default='')
parser.add_argument('-D', '--double', help="Arg to pass to the subprocess with --DOUBLE=password.", dest="double", default='')
parser.add_argument('-E', '--equal', help="Use = instead of space between single/double an arg.", action="store_true")
parser.add_argument('-R', '--repeat', help="Repeat the password with a newline to the subprocess", action="store_true")
parser.add_argument('-I', '--stdin', help="Send the password on stdin to the subprocess", action="store_true")
if sh: parser.add_argument('-B', '--bg', help="Put the subprocess into the background", action="store_true")
if keyutils:
parser.add_argument('-K', '--session_add_key', help="session_add_key description", default='', dest='session_add_key', type=str)
parser.add_argument('-P', '--pos', help="Position to place the Arg to the subprocess with --DOUBLE/SINGLE=password.", dest="pos", type=int, default=1)
parser.add_argument('-H', '--header', help="Header line as a prompt (default \"\").", dest="header", default='')
parser.add_argument('-A', '--answer', help="Skip the grid and give the answer (for testing only).", dest="answer", default='')
parser.add_argument('-O', '--output_fd', help="Output file descriptor to print the answer (default 1).", dest="output_fd", type=int, default=1)
parser.add_argument('lsargs', help="Arguments to run the subprocess wih the password in stdin.", type=str, default='', nargs='*') # metavar='STDIN COMMAND',
return parser
def make_grid(args):
"""Define character sets"""
punct = spaceout(string.punctuation)
punct2 = punct[52:]
punct = punct[:52]
whitespace = spaceout(string.whitespace)
#Select character sets
#TODO: Can this be simplified? args['u'+'l'] or whatnot?
#use_uppercases = (-1 != charsets.find("u"))
use_uppercases = args['u']
use_lowercases = args['l']
use_bindigits = args['b']
use_octdigits = args['o']
use_decdigits = args['d']
use_hexdigits = args['f']
use_punct = args['p']
use_whitespace = args['w']
_setcount = args['u']+args['l']+args['b']+args['o']+args['d']+args['f']+args['p']+args['w']
#if use_uppercases+use_lowercases+use_bindigits+use_octdigits+use_decdigits+use_hexdigits+use_punct+use_whitespace == 0:
if _setcount == 0:
use_uppercases = 1
use_lowercases = 1
use_decdigits = 1
use_punct = 1
use_whitespace = 1
use_numbers = 0
numbers = ''
if use_bindigits+use_octdigits+use_decdigits+use_hexdigits: use_numbers = 1
if use_bindigits: numbers = '0 1'
if use_octdigits: numbers = spaceout(string.octdigits)
if use_decdigits: numbers = spaceout(string.digits)
if use_hexdigits: numbers = spaceout(string.hexdigits)
##### Build static grid #####
grid = []
if use_uppercases:
if PY3:
grid.append(spaceout(string.ascii_uppercase))
else:
grid.append(spaceout(string.uppercase))
if use_lowercases:
if PY3:
grid.append(spaceout(string.ascii_lowercase))
else:
grid.append(spaceout(string.lowercase))
if use_numbers: grid.append(numbers)
if use_punct:
grid.append(punct)
grid.append(punct2)
return (use_uppercases,
use_lowercases,
use_decdigits,
use_punct,
use_whitespace,
punct,
punct2,
whitespace,
numbers,
grid, )
##### Build user interface #####
usagebanner = "Choose from the grid by typing letter pairs like eF or Dg."
#? not hasattr(sys, 'frozen') and
if Dialog is not None and os.path.isfile('/usr/bin/dialog'):
shortcuts = "5:Shell 6:FileName 7:FileContents 8:Hide 9:Quit 0:Done"
else:
shortcuts = "7:Delete 8:Show/Hide 9:Quit 0:Done"
if sPinentryCurses:
shortcuts = "4:PinEntry " +shortcuts
def total_rows(use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct):
return use_lowercases+use_uppercases+use_decdigits+use_whitespace+use_punct+use_punct+1
def lMain(lSysArgv):
global password, exit_code, VERBOSITY, iOUT_FD, oDIALOG, oLAST_DIR
global min_x, min_y, upperchar, lowerchar, out, hide_out
global oLAST_DIR
if 'TERM' not in os.environ or not os.environ['TERM']: os.environ['TERM'] = linux
padpos = 0
stdscr = None
password = ''
exit_code = -1
out = ''
upperchar = ''
lowerchar = ''
if PY3:
uppercase = string.ascii_uppercase
lowercase = string.ascii_lowercase
else:
uppercase = string.uppercase
lowercase = string.lowercase
# Check for required size before continuing
def check_size(stdscr):
(size_y,size_x) = stdscr.getmaxyx()
if ((size_x < min_x)|(size_y < min_y)):
error( __program__+" needs a "+str(min_x)+"x"+str(min_y)+" character area to function.\n")
error( "please adjust your terminal size and restart "+__program__+".")
sys.exit(1)
def pad_head(pad):
global min_x, min_y, upperchar, lowerchar, out, hide_out
pad.addstr(2,0,">>")
pad.addstr(2,4," "*(min_x+1))
disp_out = out
if hide_out:
disp_out = len(out)*"*"
pad.addstr(2,4,disp_out)
def pad_draw(stdscr, pad, ly, lx, hy, hx, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, title):
rv = __program__ +" "+ __version__
pad.addstr(0,0," "*(hx+1),curses.A_REVERSE)
pad.addstr(0,2,rv,curses.A_REVERSE)
pad.addstr(1,0," "*min_x)
#~ pad.addstr(0,40," ")
pad.addstr(0,40,lowerchar,curses.A_REVERSE)
pad.addstr(0,41,upperchar,curses.A_REVERSE)
pad.addstr(0,73,"SHOW")
if hide_out:
pad.addstr(0,73,"HIDE")
pad.addstr(1,0,header)
pad_head(pad)
##Draw the grid
global gridrow
gridrow = 6
gridoffset = 4
#Grid Indexes
pad.addstr(gridrow-2,gridoffset,spaceout(col_index))
for t in range(0, total_rows(use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct)):
pad.addstr(gridrow+t,1,row_index[t])
t += 1
#Index selection highlighting
if len(upperchar):
stdscr.addstr(gridrow-2,4+spaceout(col_index).find(upperchar),upperchar,curses.A_REVERSE)
if len(lowerchar):
stdscr.addstr(gridrow+row_index.find(lowerchar),1,lowerchar,curses.A_REVERSE)
#Static grid elements
### New grid draw method ###
r = 0
for s in grid:
pad.addstr(gridrow,gridoffset,grid[r])
r += 1
gridrow += 1
if use_whitespace:
#Draw the whitespace row
pad.addstr(gridrow,gridoffset," :Space :Tab :Enter :Clear")
pad.addstr(gridrow,gridoffset,col_index[0])
pad.addstr(gridrow,9+gridoffset,col_index[1])
pad.addstr(gridrow,18+gridoffset,col_index[2])
pad.addstr(gridrow,27+gridoffset,col_index[3])
#If the corresponding columns are selected, highlight them
if len(upperchar)&(upperchar==col_index[0]):
stdscr.addstr(gridrow,4+col_index.find(upperchar),upperchar,curses.A_REVERSE)
if len(upperchar)&(upperchar==col_index[1]):
stdscr.addstr(gridrow,12+col_index.find(upperchar),upperchar,curses.A_REVERSE)
if len(upperchar)&(upperchar==col_index[2]):
stdscr.addstr(gridrow,20+col_index.find(upperchar),upperchar,curses.A_REVERSE)
if len(upperchar)&(upperchar==col_index[3]):
stdscr.addstr(gridrow,28+col_index.find(upperchar),upperchar,curses.A_REVERSE)
gridrow += 1
#Draw the 'done' line, and highlight column if selected
pad.addstr(gridrow,gridoffset," :Ignore :Double :Triple :Delete")
pad.addstr(gridrow,gridoffset,col_index[0])
pad.addstr(gridrow,9+gridoffset,col_index[1])
pad.addstr(gridrow,18+gridoffset,col_index[2])
pad.addstr(gridrow,27+gridoffset,col_index[3])
if len(upperchar)&(upperchar==col_index[0]):
stdscr.addstr(gridrow,4+spaceout(col_index).find(upperchar),upperchar,curses.A_REVERSE)
if len(upperchar)&(upperchar==col_index[1]):
stdscr.addstr(gridrow,11+spaceout(col_index).find(upperchar),upperchar,curses.A_REVERSE)
if len(upperchar)&(upperchar==col_index[2]):
stdscr.addstr(gridrow,18+spaceout(col_index).find(upperchar),upperchar,curses.A_REVERSE)
if len(upperchar)&(upperchar==col_index[3]):
stdscr.addstr(gridrow,25+spaceout(col_index).find(upperchar),upperchar,curses.A_REVERSE)
#Help banners
pad.addstr(14,0,title)
pad.addstr(15,0,usagebanner)
# (1+hx-len(shortcuts))*' '+
pad.addstr(16,0,shortcuts,curses.A_REVERSE)
pad.refresh(padpos, 0, ly, lx, hy, hx)
def main_draw(stdscr, pad, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, title):
(max_y, max_x,) = stdscr.getmaxyx()
pad_draw(stdscr, pad, 0, 0, (max_y-2), (max_x-1), use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, title)
stdscr.move(max_y-1,0)
try:
curses.curs_set(0)
except:
pass
stdscr.refresh()
def cycle_index(cyclemethod=3): #Argument specifies method, default is 3
global col_index, row_index
if PY3:
uppercase = string.ascii_uppercase
lowercase = string.ascii_lowercase
else:
uppercase = string.uppercase
lowercase = string.lowercase
#Method 0: Ordered letters and numbers, no randomization
#This method has 1 possible state and provides no security.
#Included to allow gridfire's use to limit possible symbols entered.
#For example, hex digits only when entering a WEP key.
if cyclemethod==0:
col_index = uppercase
row_index = lowercase
#Method 1: Ordered letters, random offset (as with gpggrid)
#The math might differ from gpggrid
#This method has 676 or 26^2 possible states
if cyclemethod==1:
offset = random.randint(0,25)
col_index = uppercase[offset:]+uppercase[:offset]
offset = random.randint(0,25)
row_index = lowercase[offset:]+lowercase[:offset]
#Method 2: use random.shuffle() to shuffle one index in place
#This might not work very well, see module documentation on
#issues with small len(x) exceeding period of RNGs
# http://docs.python.org/2/library/random.html
##This method has:
##403,291,461,126,605,635,584,000,000
##or 26! possible states (403.3 septillion)
if cyclemethod==2:
col_index = uppercase
colcycle = list(uppercase)
random.shuffle(colcycle)
col_index = ''.join(colcycle)
row_index = col_index.lower()
#Method 3: use random.shuffle() to shuffle indices in place
#This might not work very well, see module documentation on
#issues with small len(x) exceeding period of RNGs
# http://docs.python.org/2/library/random.html
#This method has:
#162,644,002,617,632,464,507,038,883,409,628,607,021,056,000,000,000,000
##or 26!^2 possible states (162.6 sexdecillion).
if cyclemethod==3:
col_index = uppercase
colcycle = list(uppercase)
random.shuffle(colcycle)
col_index = ''.join(colcycle)
row_index = lowercase
rowcycle = list(lowercase)
random.shuffle(rowcycle)
row_index = ''.join(rowcycle)
#TODO: Implement method 4 (mixed cases in row/column indexes)
# (This would require redoing input handling to recognize same-case pairs)
#Method 4 would have:
#80,658,175,170,943,878,571,660,636,856,403,766,975,289,505,440,883,277,824,000,000,000,000
#or 52! possible states (80.6 Unvigintillion).
def quit_scr(leave_message = "", exit_status= 0):
global password, exit_code
stdscr.keypad(0)
curses.echo()
curses.nocbreak()
curses.endwin()
# print leave_message
password = leave_message
exit_code = exit_status
parser = make_parser()
args = vars(parser.parse_args(lSysArgv))
VERBOSITY = args['verbosity']
cyclemethod = args['method'] or 3
header = args['header'] or ''
answer = args['answer'] or ''
output_fd = args['output_fd'] or 1
lSargs = args['lsargs']
if output_fd != 1:
iOUT_FD = os.fdopen(int(output_fd), 'w')
info('gridfire PARSE lSargs=' + repr(lSargs))
debug('gridfire PARSE args.keys=' + repr(args.keys()))
#? hangs if called as a sushell sys.stdout.flush()
single = args['single'] or ''
double = args['double'] or ''
bRepeat = args['repeat']
bStdin = args['stdin']
bEqual = args['equal']
if sh: bBg = args['bg']
else: bBg = None
if single and bRepeat:
raise RuntimeError('ERROR: single and repeat ' )
if double and bRepeat:
raise RuntimeError('ERROR: double and repeat ' )
if single and bRepeat:
raise RuntimeError('ERROR: single and repeat ' )
if double and bStdin:
raise RuntimeError('ERROR: double and stdin ' )
if double and single:
raise RuntimeError('ERROR: double and single ' )
(use_uppercases,
use_lowercases,
use_decdigits,
use_punct,
use_whitespace,
punct,
punct2,
whitespace,
numbers,
grid ) = make_grid(args)
if answer:
# for testing - bypass the grid and accept the answer
password = answer
exit_code = 0
else:
if not stdscr:
stdscr = curses.initscr()
curses.noecho()
curses.start_color()
try:
curses.use_default_colors()
except:
pass
stdscr.keypad(1)
for key, value in _curses.__dict__.items():
if key[0:4] == 'ACS_' or key in ('LINES', 'COLS'):
if key == 'LINES':
value = value - 3
setattr(curses, key, value)
curses.flushinp ()
check_size(stdscr)
pad = curses.newpad(100,100)
if args['ttyalert'] == 'beep':
curses.beep()
elif args['ttyalert'] == 'flash':
curses.flash()
##### MAIN LOOP: draw the interface and handle user input #####
cycle_index(cyclemethod) #Don't start the program with unrandomized indexes
# If this isn't done, the program initially shows nothing
main_draw(stdscr, pad, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, '')
while exit_code == -1:
title = stderr_get_buffer().strip()
if title:
title = title.split('\n')[-1]
main_draw(stdscr, pad, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, title)
curses.noecho()
curses.cbreak()
sys.stdout.flush()
try:
c = stdscr.getkey()
except:
quit_scr()
break
if c == '0':
quit_scr(out) #on 'Done', write output and exit
break
if c in ['', '9', chr(27)]: # Quit
quit_scr() #on 'Cancel', exit without writing output
break
if c == '8': # Hide
hide_out -= hide_out+hide_out
hide_out += 1
elif ((c == '')&(len(out)>=1)) or ((c == 'KEY_BACKSPACE')&(len(out)>=1)):
out = out[:len(out)-1] # Delete
elif Dialog is not None and c == '7': # File Contents
_curses.def_prog_mode() ; _curses.endwin()
sOut = sDialogFget(bContents=True)
if sOut == '':
continue
sBut = 'ok'; sCode = '='
if sOut and out:
sText = sOut # "New text at Beginning, Replace or End"
sBut, sCode = lDialogCode(sText)
if sBut != 'ok':
pass
elif sCode == '<':
out = sOut + ' ' + out
elif sCode == '>':
out = out + ' ' + sOut
elif sCode == '=':
out = sOut
elif sCode == '':
out = ''
pad_head(pad)
# oDIALOG.msgbox("Bug Alert: If the screen goes black, suspend to shell with ^Z and then use fg to come back to Gridfire " +sCode, height=8, width=40)
main_draw(stdscr, pad, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, '')
_curses.reset_prog_mode()
stdscr.refresh()
elif Dialog is not None and c == '6': # File Name
# you will first need to save the tty modes with a call to def_prog_mode()
# and then call endwin() to end the curses mode. This will leave you in the
# original tty mode. To get back to curses once you are done, call
# reset_prog_mode() . This function returns the tty to the state stored by
# def_prog_mode(). Then do refresh(), and you are back to the curses mode.
_curses.def_prog_mode() ; _curses.endwin()
sOut = sDialogFget(bContents=False)
sBut = 'ok'; sCode = '='
if sOut and out:
sText = sOut # "New text at Beginning, Replace or End"
sBut, sCode = lDialogCode(sText)
if sBut != 'ok':
pass
elif sCode == '<':
out = sOut + ' ' + out
elif sCode == '>':
out = out + ' ' + sOut
elif sCode == '=':
out = sOut
elif sCode == '':
out = ''
# oDIALOG.msgbox("Bug Alert: If the screen goes black, suspend to shell with ^Z and then use fg to come back to Gridfire " +sCode, height=8, width=40)
main_draw(stdscr, pad, use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct, header, '')
_curses.reset_prog_mode()
stdscr.refresh()
elif Dialog is not None and c == '5': # Shell
if oDIALOG is None:
oDIALOG = Dialog()
sCode = '='
sOut = ''
yStdoutdata = yStderrdata = b''
# you will first need to save the tty modes with a call to def_prog_mode()
# and then call endwin() to end the curses mode. This will leave you in the
# original tty mode. To get back to curses once you are done, call
# reset_prog_mode() . This function returns the tty to the state stored by
# def_prog_mode(). Then do refresh(), and you are back to the curses mode.
_curses.def_prog_mode() ; _curses.endwin()
sCode, sAnswer = oDIALOG.inputbox("Run command (erase to cancel)",
init='')
if sCode == 'ok' and sAnswer:
with devnull_context:
p = subprocess.Popen(sAnswer,
stdout=subprocess.PIPE,
stderr=devnull, close_fds=True)
if True:
( yStdoutdata, yStderrdata ) = p.communicate()
retcode = p.returncode
oDIALOG.scrollbox(yStdoutdata.decode().strip(),
15,50)
else:
retcode = p.wait()
ystdoutdata = p.stdout.read()
# One could use title=... instead of text=... to put the text
# in the title bar.
oDIALOG.programbox(fd=p.stdout.fileno(),
text="Output of command: " +sAnswer)
# oDIALOG.msgbox("Bug Alert: If the screen goes black, suspend to shell with ^Z and then use fg to come back to Gridfire " +str(retcode), height=8, width=40)
if retcode == 0:
sOut = yStdoutdata.decode().strip()
sBut = 'ok'; sCode = '='
if sOut and out:
sText = sOut # "New text at Beginning, Replace or End"
sBut, sCode = lDialogCode(sText)
if sBut != 'ok':
pass
elif sCode == '<':
out = sOut + ' ' + out
elif sCode == '>':
out = out + ' ' + sOut
elif sCode == '=':
out = sOut
elif sCode == '':
out = ''
_curses.reset_prog_mode()
stdscr.refresh()
elif c == '4' and sPinentryCurses:
_curses.def_prog_mode() ; _curses.endwin()
iCode, sOut = pinentry_assuan_getpass()
if iCode == 0:
out += sOut
pad_head(pad)
_curses.reset_prog_mode()
stdscr.refresh()
elif c == '2': # was Ignore
# now 'Ignore' is a keypair to fool keystroke counting
pass # out = out*2
elif PinEntry is not None and c == '1': # unused
if oPinEntry is None:
oPinEntry = PinEntry()
oPinEntry.run()
# unfinished
#handle row/column selections
elif (uppercase.find(c)+1):
upperchar = c
elif (row_index[:total_rows(use_lowercases, use_uppercases, use_decdigits, use_whitespace, use_punct)].find(c)+1):
lowerchar = c
#if selection completes a pair, find choice, add to output, reset selection
if len(upperchar)&len(lowerchar):
# Ignore Double Triple
if((upperchar==col_index[0])&(lowerchar==row_index[len(grid)+1])):
#on 'Ignore', do nothing
pass
elif((upperchar==col_index[1])&(lowerchar==row_index[len(grid)+1])):
# Double
out += out
elif((upperchar==col_index[2])&(lowerchar==row_index[len(grid)+1])):
# Triple
out = 3*out
elif((upperchar==col_index[3])&(lowerchar==row_index[len(grid)+1])):
out = out[:len(out)-1] # Delete
# :Space :Tab :Enter :Clear
elif((upperchar==col_index[0])&(lowerchar==row_index[len(grid)])):
out += ' ' #on 'Space'
elif((upperchar==col_index[1])&(lowerchar==row_index[len(grid)])):
out += ' ' #on 'Tab'
elif((upperchar==col_index[2])&(lowerchar==row_index[len(grid)])):
out += '\n' #on 'Enter'
elif((upperchar==col_index[3])&(lowerchar==row_index[len(grid)])):
out = '' #on 'Clear'
pad_head(pad)
else:
rowchoice = row_index.find(lowerchar)
if rowchoice < len(grid):
if (grid[rowchoice].find("A") == 0):
out += uppercase[col_index.find(upperchar)]
if (grid[rowchoice].find("a") == 0):
out += lowercase[col_index.find(upperchar)]
if (grid[rowchoice].find("0") == 0)&(spaceout(col_index).find(upperchar) < len(numbers)):
out += numbers[spaceout(col_index).find(upperchar)]
if (grid[rowchoice].find("!") == 0):
out += punct[spaceout(col_index).find(upperchar)]
if (grid[rowchoice].find("_") == 0)&(spaceout(col_index).find(upperchar) < len(punct2)):
out += punct2[spaceout(col_index).find(upperchar)]
upperchar = ''
lowerchar = ''
cycle_index(cyclemethod)
debug('END MAINLOOP lSargs=' +repr(lSargs) +' len=' +str(len(password)) \
+' exit_code=' +str(exit_code))
sRetval = ''
if exit_code == 0 and lSargs and password:
try:
if os.uname().sysname == 'Linux':
sDash = '-'
else:
# veracrypt win requires /
sDash = '/'
sStdin = ''
lThings = []
if single:
# trouble- password=shlex.quote(password)
if bEqual:
lThings = [sDash+single +'=' +password]
else:
lThings = [sDash+single, password]
elif double:
# truecrypt requires =
if bEqual:
lThings = ['--'+double +'=' +password]
else:
lThings = ['--'+double, password]
elif bRepeat:
sStdin = password +'\n' +password
else:
sStdin = password
if lThings:
pos = args['pos']
if pos == 0:
largs = lThings + lSargs
elif pos == -1:
largs = lSargs + lThings
else:
largs = lSargs[0:pos] +lThings + lSargs[pos:]
else:
largs = lSargs
try:
if largs.index('--') >= 0:
del largs[largs.index('--')]
except ValueError: pass
# if the password contains a space assume the first elt is
# the password and the rest are command line addons
if password.find(' ') > 0:
lCargs = shlex.split(password)
largs += lCargs[1:]
password = lCargs[0]
if keyutils:
if not args['session_add_key']:
if len(largs) > 0 and os.path.isfile(largs[-1]):
args['session_add_key'] = os.path.split(largs[-1])[-1]
if args['session_add_key']:
yVal = password.encode()
yDesc=args['session_add_key'].encode()
session_add_key(yDesc, yVal)
env = os.environ.copy()
if 'PYTHONPATH' in env:
del env['PYTHONPATH']
executable = shutil.which(largs[0])
if not executable:
raise RuntimeError('ERROR: executable not found ' +str(repr(largs[0])))
_curses.def_prog_mode() ; _curses.endwin()
info('executable=' +executable +' lArgs[1:]=%r' % (largs[1:]),)
# debug(' len(password)=%d' % len(password))
stderr_to_buffer()
global stdoutdata, stderrdata, iRetval
iRetval = 0; stdoutdata = stderrdata = ''
if bBg and sh is None:
warn("Ignoring -B--bg - no import sh")
bBg = False
elif bBg:
debug('sh sStdin bBg=True len(sStdin)=%d' % len(sStdin))
try:
# oProc=getattr(sh, executable)
oProc = __import__('sh', fromlist=[executable])
def process_output(line):
global stdoutdata, exit_code
stdoutdata += line
if exit_code != -1: return True
def process_errput(line):
sys.stderr.write('STDERR: ' +line.decode() + '\n')
global stderrdata, exit_code
stderrdata += line
if exit_code != -1: return True
oProcess=oProc(*largs[1:],
_in=StringIO(sStdin),
_out=process_output,
_err=process_errput,
_bg=True)
except Exception as e:
error('ERROR in sh.oProc ' +str(e))
iRetval = 1
password = ''
else:
shell = False # bBg or
# if bBg: largs += ['&']
debug('Popen bBg='+repr(bBg) +' len(sStdin)=%d' % len(sStdin))
try:
process = subprocess.Popen(largs,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
shell=shell)
# close_fds=True
except Exception as e:
sMsg='ERROR in Popen ' +str(e)
error(sMsg)
lDialogMessage(sMsg,height=20,width=60)
iRetval = 1
password = ''
else:
if sStdin:
(stdoutdata, stderrdata,) = process.communicate(sStdin.encode())
else:
(stdoutdata, stderrdata,) = process.communicate(None)
iRetval = process.returncode
for pipe in (process.stdin, process.stdout, process.stderr):
if pipe: pipe.close()
stderrdata=stderrdata.strip()
if stderrdata:
if hasattr (stderrdata, 'decode'):
stderrdata=stderrdata.decode()
sys.stderr.write('STDERR: ' +stderrdata + '\n')
sMess='stdoutdata=' +repr(stdoutdata) \
+' stderrdata=' +repr(stderrdata) \
+' returncode=' +str(iRetval)
# if stderrdata.find('Failed') >= 0:
# lDialogMessage(sMess,height=20,width=60)
if iRetval:
warn(sMess)
lDialogMessage(sMess,height=20,width=60)
else:
info(sMess)
if hasattr (stdoutdata, 'decode'):
sRetval = stdoutdata.decode().strip()
else:
sRetval = stdoutdata.strip()
debug('len(sRetval)=%d' % len(sRetval) +' iRetval=' +str(iRetval))
exit_code = iRetval
except Exception as e:
sMsg = 'ERROR after mainloop error=' +str(e) +' len=' +str(len(password))
error(sMsg)
lDialogMessage(sMsg)
warn(traceback.format_exc())
exit_code = 1
stderr_un_buffer()
else:
debug('NO exit_code=' +str(exit_code) +' lSargs=' +repr(lSargs) +' len(password)=' +str(len(password)))
sRetval = password
# del sRetval, exit_code
stdscr = None
cycle_index(cyclemethod) #Don't start the program with unrandomized indexes
lRetval = [exit_code, sRetval][:]
out = ''
exit_code = -1
return lRetval
def get_tty(stream=None, tty=None):
# what should stream be if curses should be a tty
if tty is not None:
pass
elif 'GPG_TTY' in os.environ:
tty = os.environ['GPG_TTY']
elif stream is not None:
tty = os.ttyname(stream.fileno())
else:
tty = os.ttyname(sys.stdin.fileno())
with open(tty, 'r') as fd:
assert fd.isatty(), "The input stream must be a tty: %r" % (tty,)
return tty
oSTDERR=StringIO()
def stderr_to_buffer():
global oSTDERR
if not hasattr(sys, '_stderr'):
sys._stderr = sys.stderr
sys.stderr = oSTDERR
def stderr_un_buffer():
if hasattr(sys, '_stderr'):
sys.stderr = sys._stderr
def stderr_get_buffer():
sys.stderr.flush()
if hasattr(sys.stderr, 'seek') and sys.stderr.seekable():
sys.stderr.seek(0)
if hasattr(sys.stderr, 'getvalue'):
title = sys.stderr.getvalue().strip()
if hasattr(sys.stderr, 'seek') and sys.stderr.seekable():
sys.stderr.seek(0)
elif hasattr(sys.stderr, 'buf'):
sys.stderr.buf = ''
else:
title = ''
return title
# should be getpass.getpass compatible
def getpass(prompt="", stream=None, tty=None, main_args=None):
tty = get_tty(stream, tty)
#?
if not hasattr(sys, '_stderr'):
sys._stderr = sys.stderr
sys.stderr = StringIO()
sRetval = ''
exit_code = -1
if main_args is None:
main_args = ['-H', prompt]
else:
main_args += ['-H', prompt]
try:
(exit_code, sRetval,) = main(main_args)
info('EXIT: getpass ' + prompt +' ' + repr(sRetval))
except KeyboardInterrupt:
info('EXIT: getpass KeyboardInterrupt')
#?
if hasattr(sys, '_stderr'):
sys.stderr = sys._stderr
return sRetval
def session_add_key(yDesc,yVal):
from keyutils import join_session_keyring, add_key, request_key, set_perm
iSk=join_session_keyring()
assert type(iSk) == int
iKey = add_key(yDesc, yVal, iSk)
assert iKey >= 0
key_id=request_key(yDesc, iSk)
assert key_id > 0
set_perm(key_id, 0x3f1f0000)
info('session_add_key(' +yDesc.decode() +')')
def sDialogFget(bContents=True):
global oLAST_DIR
sBut, sCode = lDialogFselect(oLAST_DIR, height=10, width=50, help_button=False)
sOut = ''
if sBut == 'ok':
sFile = sCode
if bContents is not True:
return sFile
try:
with open( sFile, 'rt') as iFd:
sOut = iFd.read().strip()
except Exception as e:
# dunno
pass
else:
if os.path.isdir(sFile):
oLAST_DIR = sFile
else:
oLAST_DIR = os.path.dirname(sFile)
oLAST_DIR = oLAST_DIR + '/'
return sOut
def lDialogMessage(sMess, height=10, width=50, help_button=False, prompt=None):
global oDIALOG, oLAST_DIR
if oDIALOG is None:
oDIALOG = Dialog()
sBut, sCode = oDIALOG.msgbox(sMess, height, width, help_button=False)
return (sBut, sCode,)
def lDialogFselect(sLastDir=None, height=10, width=50, help_button=False, prompt=None):
global oDIALOG, oLAST_DIR
if oDIALOG is None: oDIALOG = Dialog()
if not sLastDir: sLastDir = oLAST_DIR
sBut, sCode = oDIALOG.fselect(sLastDir, height, width, help_button=False)
return (sBut, sCode,)
def lDialogCode(sText):
lChoices = [("<", "Begin - Add the text at the beginning"),
("=", "All - Replace all with the text"),
(">", "End - Add the text at the end")]
sCode = '='
sBut, sCode = oDIALOG.menu(sText, height=16, width=50,
choices=lChoices,
title="Replace the buffer with file contents",
help_button=False)
return (sBut, sCode,)
def pinentry_assuan_getpass():
# simply prints out commands for pinentry's stdin to activate the
# password dialog
sStdIn='''OPTION lc-ctype=C
SETTITLE Enter the text that you want added to gridfire
SETDESC Enter the text that you want added to gridfire
SETPROMPT Text:
GETPIN
'''
sStderr = sRetval = ''
largs = [sPinentryCurses]
# -T, --ttyname FILE Set the tty terminal node name
# -N, --ttytype NAME Set the tty terminal type
# -C, --lc-ctype STRING Set the tty LC_CTYPE value
# -M, --lc-messages STRING Set the tty LC_MESSAGES value
# -o, --timeout SECS Timeout waiting for input after this many seconds
# -g, --no-global-grab Grab keyboard only while window is
tty = get_tty()
largs += ['-T', tty, '-g']
#S ERROR curses.open_tty_for_read 83918849 \\nERR 83918849 Permission denied <Pinentry>
if os.getegid() == 0 and 'SUDO_USER' in os.environ:
o=os.stat(tty)
if o.st_uid != 0:
if hasattr(shlex, 'join'):
largs =['su', '-c', shlex.join(largs), os.environ['SUDO_USER']]
else:
largs =['su', '-c', ' '.join(largs), os.environ['SUDO_USER']]
stderr_to_buffer()
try:
sStdIn='OPTION ttyname=' +tty +'\n' +sStdIn
debug('pinentry_assuan_getpass tty=' +repr(tty))
(exit_code, sStdOut,) = lSubprocess(largs, sStdIn=sStdIn)
if exit_code == 0:
for sLine in sStdOut.split('\n'):
if sLine[:2] == 'D ':
sRetval = sLine[2:]
break
elif sLine.startswith('ERR') or sLine.startswith('S ERR'):
exit_code = 1
sys.stderr.write(sLine +'\n')
warn('pinentry_assuan_getpass ' +sLine) # +'\n'
if exit_code:
warn('pinentry_assuan_getpass \nexit_code=' + str(exit_code))
else:
debug('pinentry_assuan_getpass len sRetval=' +str(len(sRetval)))
except Exception as e:
error('pinentry_assuan_getpass exception=' + str(e))
exit_code = 1
stderr_un_buffer()
return (exit_code, sRetval.strip(),)
def lSubprocess(largs, shell=False, executable='', sStdIn=''):
stderr_to_buffer()
env = os.environ.copy()
if 'PYTHONPATH' in env:
del env['PYTHONPATH']
yStdoutData = stderrdata = ''
try:
debug('lSubprocess Popen largs=' + repr(largs) +' len(Stdin)=' +str(len(sStdIn)))
oProcess = subprocess.Popen(largs,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
shell=shell)
sleep(3)
except Exception as e:
error('Popen executable=' + repr(executable) +' largs=' + repr(largs) + '\n' + str(e))
exit_code = 1
sStdOut = ''
else:
if sStdIn:
(yStdoutData, stderrdata,) = oProcess.communicate(sStdIn.encode())
else:
#? wait
(yStdoutData, stderrdata,) = oProcess.communicate()
#? oProcess.close()
if stderrdata:
sys.stderr.write(stderrdata.decode() + '\n')
exit_code = oProcess.returncode
sStdOut = yStdoutData.decode()
info('Popen len sStdOut=' +str(len(sStdOut)) +' exit_code=' + repr(exit_code) )
stderr_un_buffer()
return (exit_code, sStdOut,)
def iMain(lSysArgv=None):
if not lSysArgv: lSysArgv=sys.argv[1:]
try:
stderr_to_buffer()
(exit_code, password,) = lMain(lSysArgv)
stderr_un_buffer()
except Exception as e:
lDialogMessage('ERROR: IMAIN ' + str(e))
exit_code = 1
password = ''
warn('ERROR: Leaving lIMain exit_code=1 ' + str(e))
debug(traceback.format_exc())
else:
debug('leaving lIMain exit_code=' + repr(exit_code))
sys.stderr.flush()
iOUT_FD.write(password+('\n'*newline))
iOUT_FD.flush()
password = ''
return exit_code
def main():
sys.exit(iMain())
if __name__ == '__main__':
main()
if False:
lSargs = []
if lSargs:
if type(lSargs) == str:
#? quote - unused - it's always a list
largs = lSargs.split(' ')
elif type(lSargs) == list and len(lSargs) == 1 and lSargs[0].find(' ') > 0:
largs = lSargs[0].strip().split()
elif type(lSargs) == list and len(lSargs) > 1:
largs = lSargs
else:
raise RuntimeError('ERROR: not list or str type(lSargs) ' + str(type(lSargs) ))