0

I am having this error "sudo: no tty present and no askpass program spcified" when I run the code. It's the line with 2nd subprocesscall, where I try to change the password. The user gets created perfectly, but the password is not, just gives the given error. What I am trying to do, that after you create user, the written password would be given to the user.

import subprocess

def new(username, password):
    subprocess.call("echo passd23 | sudo -S adduser '{username}'".format(username=username), shell=True)
    subprocess.call("echo passd23 | sudo '{username}':'{password}' | chpasswd".format(username=username, password=password), shell=True)

new("username", "password")    
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Lenton
  • 479
  • 1
  • 3
  • 13
  • `sudo` doesn't read passwords from stdin -- and it *shouldn't*, because your `echo passd23` is innately insecure. – Charles Duffy May 13 '17 at 14:49
  • ...so, the behavior is correctly preventing you from doing this wildly insecure thing, which exposes your password to every program on your system that can read the process table. – Charles Duffy May 13 '17 at 14:49
  • (Speaking of wildly insecure -- A password can **contain** literal quotes, so attempting to use literal quotes to escape a shell password in code directly substituted into a shell command line is foolhardy. If a new user specifies their password in this GUI as `'$(rm -rf /*)'` -- making the command `sudo 'username':''$(rm -rf /*)''`, where the quotes opened in hardcoded code are closed inside the password itself, you're going to have a very, **very** bad day). – Charles Duffy May 13 '17 at 14:51
  • @Charles Duffy I did not write down the real password in here, I made it up, but I wrote it more like an example of how my code is, none of the info here should be taken seriously :) And it's all more for college, not useage for company – Lenton May 13 '17 at 14:53
  • It's not being published in StackOverflow that makes it insecure. It's being published in the operating system's process listing (readable by every user on the system, including `nobody` -- an account used for unauthenticated and untrusted code) that makes the password insecure. Passwords should **never** be passed on command lines. – Charles Duffy May 13 '17 at 14:54
  • ...what matters, I suppose, is what class you're taking; if it were an infosec course (typically a 300-level class, IIRC?), I'd be grading this very harshly. Even then, it's worth being cautious of what you let yourself learn: Things you do become habits, and someday you *will* be working at a company. – Charles Duffy May 13 '17 at 14:56
  • That said -- the second invocation has a `sudo` without a `-S` (directing it to honor stdin), *and* without a command being passed. What's the password whose privileges you *actually* want to escalate? `chpasswd`? Then it should be `sudo -S chpasswd`. (And btw -- instead of `echo | ...`, use `stdin=subprocess.PIPE`, and write to that stdin from Python code rather than shell; that way the password isn't ever on a command line). – Charles Duffy May 13 '17 at 15:00
  • I've attempted to edit this towards making it a [mcve]. Some further edits to distinguish it as not a dupe of, say, [How can I set a user's password in Linux from a Python script](http://stackoverflow.com/questions/4688441/how-can-i-set-a-users-password-in-linux-from-a-python-script) or [Using sudo with Python script](http://stackoverflow.com/questions/13045593/using-sudo-with-python-script) are probably still called for. – Charles Duffy May 13 '17 at 15:06
  • Well, it appears that now the user is created and his password is changed, but he is still disabled. When changing same way password in terminal, the user gets enabled once password is set – Lenton May 13 '17 at 16:01
  • If the question you asked is answered, then what you still have is a different question. – Charles Duffy May 13 '17 at 16:26

1 Answers1

0

This is bad practice, but it's safer than what it replaces:

def new(username, password, sudopass):
    p1 = subprocess.Popen(['sudo', '-S', 'adduser', username], stdin=subprocess.PIPE)
    p1.communicate(sudopass)
    if p1.retval != 0:
        return False
    p2 = subprocess.Popen(['sudo', '-S', 'chpasswd'], stdin=subprocess.PIPE)
    p2.communicate('%s\n%s:%s\n' % (sudopass, username, password))
    return p2.retval == 0

new('username', 'password', 'passd23')

Notably:

  • No user-provided strings whatsoever are parsed by a shell as code. The sudo password is passed as stdin, not on a generated command line, and the user-provided username and password in particular are never evaluated by a shell.
  • With chpasswd, both the sudo password and then the user's username:password are provided on stdin, one after the other.

That said -- in a real-world example, your script shouldn't have a plaintext password in memory at all; instead, it should be configured in /etc/sudoers to have passwordless privilege escalation.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441