1239 lines
46 KiB
Python
Executable File
1239 lines
46 KiB
Python
Executable File
#!/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) ))
|
||
|