summaryrefslogtreecommitdiff
path: root/core/opie-login/passworddialogimpl.cpp
blob: 3c1b474499a3422c2b716d7dbdfe7c1d0d1c0fb5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/*
               =.            This file is part of the OPIE Project
             .=l.            Copyright (c)  2004 Holger Hans Peter Freyther <zecke@handhelds.org>
           .>+-=
 _;:,     .>    :=|.         This file is free software; you can
.> <`_,   >  .   <=          redistribute it and/or modify it under
:`=1 )Y*s>-.--   :           the terms of the GNU General Public
.="- .-=="i,     .._         License as published by the Free Software
 - .   .-<_>     .<>         Foundation; either version 2 of the License,
     ._= =}       :          or (at your option) any later version.
    .%`+i>       _;_.
    .i_,=:_.      -<s.       This file 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 General
..}^=.=       =       ;      Public License for more details.
++=   -.     .`     .:
 :     =  ...= . :.=-        You should have received a copy of the GNU
 -.   .:....=;==+<;          General Public License along with this file;
  -_. . .   )=.  =           see the file COPYING. If not, write to the
    --        :-=`           Free Software Foundation, Inc.,
                             59 Temple Place - Suite 330,
                             Boston, MA 02111-1307, USA.

*/

#include <qlayout.h>
#include <qlabel.h>
#include <qlineedit.h>
#include <qvalidator.h>
#include <qmessagebox.h>
#include <qhbox.h>
#include <qtoolbutton.h>



#include <sys/types.h>
#include <pwd.h>
#include <shadow.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>

// Shitty gcc2 toolchain
extern "C" char* crypt( const char*, const char* );



#include "passworddialogimpl.h"


// This function is taken from 'busybox'.
static int i64c(int i) {
    if (i <= 0)
        return ('.');
    if (i == 1)
        return ('/');
    if (i >= 2 && i < 12)
        return ('0' - 2 + i);
    if (i >= 12 && i < 38)
        return ('A' - 12 + i);
    if (i >= 38 && i < 63)
        return ('a' - 38 + i);
    return ('z');
}

// This function is taken from 'busybox'.
static char *crypt_make_salt() {
    time_t now;
    static unsigned long x;
    static char result[3];

    ::time(&now);
    x += now + getpid() + clock();
    result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
    result[1] = i64c(((x >> 12) ^ x) & 077);
    result[2] = '\0';
    return result;
}

/*
 * Modal dialog to force root password. It is quite hard as it only leave
 * when a password is set.
 * Also it prevalidates the password...
 */
PasswordDialogImpl::PasswordDialogImpl( QWidget* parent )
    : PasswordDialog( parent, 0, true ), m_isSet( PasswordDialogImpl::needDialog() ) {
}

PasswordDialogImpl::~PasswordDialogImpl() {
    /* qt does the stuff for us */
}

void PasswordDialogImpl::done(int res) {
    m_isSet = true;

    /*
     * The user hit 'Ok' see if we can safe the file
     * if not an error will be raised and m_isSet altered.
     * On cancel we will see if it is now ok...
     */
    if ( res == Accepted )
        writePassword();
    else if(PasswordDialogImpl::needDialog() ) {
        switch( QMessageBox::warning(this,tr("Trying to leave without password set") ,
                                     tr("<qt>No password was set. This could lead to you not beeing"
                                        "able to remotely connect to your machine."
                                        "Do you want to continue not setting a password?</qt>" ),
                                     QMessageBox::Ok, QMessageBox::Cancel ) ) {
        case QMessageBox::Cancel:
            m_isSet = false;
            break;
        case  QMessageBox::Ok:
        default:
            break;
        }

    }

    if(m_isSet)
        PasswordDialog::done( res );
}

/*
 * Lets see if we can write either shadow
 *
 */
/**
 * CRYPT the password and then tries to write it either to the shadow password
 * or  to the plain /etc/passwd
 */
void PasswordDialogImpl::writePassword() {
    /*
     * Check if both texts are the same
     */
    if ( m_pass->text() != m_confirm->text() )
        return error( tr("Passwords don't match"),
                      tr("<qt>The two passwords don't match. Please try again.</qt>") );


    /*
     * Now crypt the password so we can write it later
     */
    char* password = ::crypt( m_pass->text().latin1(), crypt_make_salt() );

    if ( !password )
        return error( tr("Password not legal" ),
                      tr("<qt>The entered password is not a valid password."
                         "Please try entering a valid password.</qt>" ) );

    /* rewind and rewrite the password file */
    ::setpwent();

    FILE* file = ::fopen( "/etc/passwd.new", "w" );
    struct passwd* pass;
    while ( (pass = ::getpwent()) != 0l ) {
        /* no  shadow password support */
        if ( pass->pw_uid == 0  )
            pass->pw_passwd = password;

        ::putpwent( pass, file );
    }

    ::fclose( file );
    ::endpwent();
    if (::rename("/etc/passwd.new","/etc/passwd" ) == -1)
	return error( tr("Rename /etc/passwd failed"),
                      tr("<qt>Renaming /etc/passwd.new to /etc/passwd failed."
			 "Please check your /etc/passed file, your /etc directory "
			 "or your filesystem.</qt>") );

    /* should be done now */
#ifdef  OPIE_LOGIN_SHADOW_PW
    #error "Can't write Shadow Passwords fixme"
#endif
}

/**
 * Raise an error. Delete input and set the focus after showing
 * the error to the user
 */
void PasswordDialogImpl::error( const QString& caption, const QString& text ) {
    m_isSet = false;
    QMessageBox::critical(this,caption, text,
                          QMessageBox::Ok, QMessageBox::NoButton );

    m_pass->setText("");
    m_pass->setFocus();

    m_confirm->setText("");
}

void PasswordDialogImpl::slotToggleEcho( bool b ) {
    m_pass->   setEchoMode( b ? QLineEdit::Normal : QLineEdit::Password );
    m_confirm->setEchoMode( b ? QLineEdit::Normal : QLineEdit::Password );
}

/////////////////////////
/// static functions
///

/**
 * Ask whether or not we need to show the dialog. It returns true if
 * no root password is set so that the user will be able to set one.
 */
bool PasswordDialogImpl::needDialog() {
   /*
    * This can cope with no password and shadow passwords
    * Let us go through the user database until we find  'root' and then
    * see if it is 'shadow' and see if shadow is empty or see if the password is empty
    */
    bool need = false;
    struct passwd *pwd;
    ::setpwent();

    while((pwd = ::getpwent() ) ) {
        /* found root */
        if( pwd->pw_uid == 0 ) {
            QString str = QString::fromLatin1(pwd->pw_passwd );

            /*
             * If str is really empty it is passwordless anyway... or '*' is a hint to set one
             * on OE/Familiar
             * else it is shadow based
             */
            if(str.isEmpty() || str == '*' )
                need = true;
            else if ( str == 'x' )
#ifdef OPIE_LOGIN_SHADOW_PW
                need =  QString::fromLatin1( ::getspnam( pwd->pw_name )->sp_pwdp ).isEmpty();
#else
            ;
#endif
            break;
        }
    }
    ::endpwent();

    return need;
}