Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
M
Magisk
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Magisk
Commits
d119dd9a
Commit
d119dd9a
authored
Oct 04, 2018
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rewrite su daemon and client
parent
09ef19f7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
345 additions
and
409 deletions
+345
-409
daemon.c
native/jni/daemon/daemon.c
+1
-1
socket.c
native/jni/daemon/socket.c
+9
-7
daemon.h
native/jni/include/daemon.h
+2
-2
connect.c
native/jni/su/connect.c
+24
-24
su.c
native/jni/su/su.c
+138
-165
su.h
native/jni/su/su.h
+11
-16
su_daemon.c
native/jni/su/su_daemon.c
+160
-194
No files found.
native/jni/daemon/daemon.c
View file @
d119dd9a
...
@@ -67,7 +67,7 @@ static void *request_handler(void *args) {
...
@@ -67,7 +67,7 @@ static void *request_handler(void *args) {
ls_hide_list
(
client
);
ls_hide_list
(
client
);
break
;
break
;
case
SUPERUSER
:
case
SUPERUSER
:
su_daemon_
receiv
er
(
client
,
&
credential
);
su_daemon_
handl
er
(
client
,
&
credential
);
break
;
break
;
case
CHECK_VERSION
:
case
CHECK_VERSION
:
write_string
(
client
,
xstr
(
MAGISK_VERSION
)
":MAGISK"
);
write_string
(
client
,
xstr
(
MAGISK_VERSION
)
":MAGISK"
);
...
...
native/jni/daemon/socket.c
View file @
d119dd9a
...
@@ -39,7 +39,7 @@ int create_rand_socket(struct sockaddr_un *sun) {
...
@@ -39,7 +39,7 @@ int create_rand_socket(struct sockaddr_un *sun) {
return
fd
;
return
fd
;
}
}
int
socket_accept
(
int
s
erv_
fd
,
int
timeout
)
{
int
socket_accept
(
int
s
ock
fd
,
int
timeout
)
{
struct
timeval
tv
;
struct
timeval
tv
;
fd_set
fds
;
fd_set
fds
;
int
rc
;
int
rc
;
...
@@ -47,16 +47,16 @@ int socket_accept(int serv_fd, int timeout) {
...
@@ -47,16 +47,16 @@ int socket_accept(int serv_fd, int timeout) {
tv
.
tv_sec
=
timeout
;
tv
.
tv_sec
=
timeout
;
tv
.
tv_usec
=
0
;
tv
.
tv_usec
=
0
;
FD_ZERO
(
&
fds
);
FD_ZERO
(
&
fds
);
FD_SET
(
s
erv_
fd
,
&
fds
);
FD_SET
(
s
ock
fd
,
&
fds
);
do
{
do
{
rc
=
select
(
s
erv_
fd
+
1
,
&
fds
,
NULL
,
NULL
,
&
tv
);
rc
=
select
(
s
ock
fd
+
1
,
&
fds
,
NULL
,
NULL
,
&
tv
);
}
while
(
rc
<
0
&&
errno
==
EINTR
);
}
while
(
rc
<
0
&&
errno
==
EINTR
);
if
(
rc
<
1
)
{
if
(
rc
<
1
)
{
PLOGE
(
"select"
);
PLOGE
(
"select"
);
exit
(
-
1
)
;
return
-
1
;
}
}
return
xaccept4
(
s
erv_
fd
,
NULL
,
NULL
,
SOCK_CLOEXEC
);
return
xaccept4
(
s
ock
fd
,
NULL
,
NULL
,
SOCK_CLOEXEC
);
}
}
/*
/*
...
@@ -164,13 +164,15 @@ void send_fd(int sockfd, int fd) {
...
@@ -164,13 +164,15 @@ void send_fd(int sockfd, int fd) {
int
read_int
(
int
fd
)
{
int
read_int
(
int
fd
)
{
int
val
;
int
val
;
xxread
(
fd
,
&
val
,
sizeof
(
int
));
if
(
xxread
(
fd
,
&
val
,
sizeof
(
int
))
!=
sizeof
(
int
))
return
-
1
;
return
val
;
return
val
;
}
}
int
read_int_be
(
int
fd
)
{
int
read_int_be
(
int
fd
)
{
uint32_t
val
;
uint32_t
val
;
xxread
(
fd
,
&
val
,
sizeof
(
val
));
if
(
xxread
(
fd
,
&
val
,
sizeof
(
val
))
!=
sizeof
(
int
))
return
-
1
;
return
ntohl
(
val
);
return
ntohl
(
val
);
}
}
...
...
native/jni/include/daemon.h
View file @
d119dd9a
...
@@ -61,7 +61,7 @@ int check_and_start_logger();
...
@@ -61,7 +61,7 @@ int check_and_start_logger();
socklen_t
setup_sockaddr
(
struct
sockaddr_un
*
sun
,
daemon_t
d
);
socklen_t
setup_sockaddr
(
struct
sockaddr_un
*
sun
,
daemon_t
d
);
int
create_rand_socket
(
struct
sockaddr_un
*
sun
);
int
create_rand_socket
(
struct
sockaddr_un
*
sun
);
int
socket_accept
(
int
s
erv_
fd
,
int
timeout
);
int
socket_accept
(
int
s
ock
fd
,
int
timeout
);
int
recv_fd
(
int
sockfd
);
int
recv_fd
(
int
sockfd
);
void
send_fd
(
int
sockfd
,
int
fd
);
void
send_fd
(
int
sockfd
,
int
fd
);
int
read_int
(
int
fd
);
int
read_int
(
int
fd
);
...
@@ -98,6 +98,6 @@ void ls_hide_list(int client);
...
@@ -98,6 +98,6 @@ void ls_hide_list(int client);
* Superuser *
* Superuser *
*************/
*************/
void
su_daemon_
receiv
er
(
int
client
,
struct
ucred
*
credential
);
void
su_daemon_
handl
er
(
int
client
,
struct
ucred
*
credential
);
#endif
#endif
native/jni/su/connect.c
View file @
d119dd9a
...
@@ -14,86 +14,86 @@
...
@@ -14,86 +14,86 @@
#include "magisk.h"
#include "magisk.h"
#include "daemon.h"
#include "daemon.h"
#include "utils.h"
#include "su.h"
#include "su.h"
#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am"
#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am"
static
char
*
get_command
(
const
struct
su_request
*
to
)
{
static
char
*
get_command
(
const
struct
su_request
*
to
)
{
if
(
to
->
command
)
if
(
to
->
command
[
0
]
)
return
to
->
command
;
return
to
->
command
;
if
(
to
->
shell
)
if
(
to
->
shell
[
0
]
)
return
to
->
shell
;
return
to
->
shell
;
return
DEFAULT_SHELL
;
return
DEFAULT_SHELL
;
}
}
static
void
silent_run
(
char
*
const
args
[])
{
static
void
silent_run
(
char
*
const
args
[])
{
set_identity
(
0
);
if
(
fork_dont_care
())
if
(
fork
())
return
;
return
;
int
zero
=
open
(
"/dev/zero"
,
O_RDONLY
|
O_CLOEXEC
);
int
zero
=
open
(
"/dev/zero"
,
O_RDONLY
|
O_CLOEXEC
);
dup2
(
zero
,
0
);
x
dup2
(
zero
,
0
);
int
null
=
open
(
"/dev/null"
,
O_WRONLY
|
O_CLOEXEC
);
int
null
=
open
(
"/dev/null"
,
O_WRONLY
|
O_CLOEXEC
);
dup2
(
null
,
1
);
x
dup2
(
null
,
1
);
dup2
(
null
,
2
);
x
dup2
(
null
,
2
);
setenv
(
"CLASSPATH"
,
"/system/framework/am.jar"
,
1
);
setenv
(
"CLASSPATH"
,
"/system/framework/am.jar"
,
1
);
execv
(
args
[
0
],
args
);
execv
(
args
[
0
],
args
);
PLOGE
(
"exec am"
);
PLOGE
(
"exec am"
);
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
}
}
static
void
setup_user
(
char
*
user
)
{
static
void
setup_user
(
char
*
user
,
struct
su_info
*
info
)
{
switch
(
DB_SET
(
su_ctx
->
info
,
SU_MULTIUSER_MODE
))
{
switch
(
DB_SET
(
info
,
SU_MULTIUSER_MODE
))
{
case
MULTIUSER_MODE_OWNER_ONLY
:
case
MULTIUSER_MODE_OWNER_ONLY
:
case
MULTIUSER_MODE_OWNER_MANAGED
:
case
MULTIUSER_MODE_OWNER_MANAGED
:
sprintf
(
user
,
"%d"
,
0
);
sprintf
(
user
,
"%d"
,
0
);
break
;
break
;
case
MULTIUSER_MODE_USER
:
case
MULTIUSER_MODE_USER
:
sprintf
(
user
,
"%d"
,
su_ctx
->
info
->
uid
/
100000
);
sprintf
(
user
,
"%d"
,
info
->
uid
/
100000
);
break
;
break
;
}
}
}
}
void
app_log
()
{
void
app_log
(
struct
su_context
*
ctx
)
{
char
user
[
8
];
char
user
[
8
];
setup_user
(
user
);
setup_user
(
user
,
ctx
->
info
);
char
fromUid
[
8
];
char
fromUid
[
8
];
sprintf
(
fromUid
,
"%d"
,
sprintf
(
fromUid
,
"%d"
,
DB_SET
(
su_
ctx
->
info
,
SU_MULTIUSER_MODE
)
==
MULTIUSER_MODE_OWNER_MANAGED
?
DB_SET
(
ctx
->
info
,
SU_MULTIUSER_MODE
)
==
MULTIUSER_MODE_OWNER_MANAGED
?
su_ctx
->
info
->
uid
%
100000
:
su_
ctx
->
info
->
uid
);
ctx
->
info
->
uid
%
100000
:
ctx
->
info
->
uid
);
char
toUid
[
8
];
char
toUid
[
8
];
sprintf
(
toUid
,
"%d"
,
su_ctx
->
to
.
uid
);
sprintf
(
toUid
,
"%d"
,
ctx
->
req
.
uid
);
char
pid
[
8
];
char
pid
[
8
];
sprintf
(
pid
,
"%d"
,
su_
ctx
->
pid
);
sprintf
(
pid
,
"%d"
,
ctx
->
pid
);
char
policy
[
2
];
char
policy
[
2
];
sprintf
(
policy
,
"%d"
,
su_
ctx
->
info
->
access
.
policy
);
sprintf
(
policy
,
"%d"
,
ctx
->
info
->
access
.
policy
);
char
*
cmd
[]
=
{
char
*
cmd
[]
=
{
AM_PATH
,
"broadcast"
,
AM_PATH
,
"broadcast"
,
"-a"
,
"android.intent.action.BOOT_COMPLETED"
,
"-a"
,
"android.intent.action.BOOT_COMPLETED"
,
"-p"
,
DB_STR
(
su_
ctx
->
info
,
SU_MANAGER
),
"-p"
,
DB_STR
(
ctx
->
info
,
SU_MANAGER
),
"--user"
,
user
,
"--user"
,
user
,
"--es"
,
"action"
,
"log"
,
"--es"
,
"action"
,
"log"
,
"--ei"
,
"from.uid"
,
fromUid
,
"--ei"
,
"from.uid"
,
fromUid
,
"--ei"
,
"to.uid"
,
toUid
,
"--ei"
,
"to.uid"
,
toUid
,
"--ei"
,
"pid"
,
pid
,
"--ei"
,
"pid"
,
pid
,
"--ei"
,
"policy"
,
policy
,
"--ei"
,
"policy"
,
policy
,
"--es"
,
"command"
,
get_command
(
&
su_ctx
->
to
),
"--es"
,
"command"
,
get_command
(
&
ctx
->
req
),
NULL
NULL
};
};
silent_run
(
cmd
);
silent_run
(
cmd
);
}
}
void
app_connect
(
const
char
*
socket
)
{
void
app_connect
(
const
char
*
socket
,
struct
su_info
*
info
)
{
char
user
[
8
];
char
user
[
8
];
setup_user
(
user
);
setup_user
(
user
,
info
);
char
*
cmd
[]
=
{
char
*
cmd
[]
=
{
AM_PATH
,
"broadcast"
,
AM_PATH
,
"broadcast"
,
"-a"
,
"android.intent.action.BOOT_COMPLETED"
,
"-a"
,
"android.intent.action.BOOT_COMPLETED"
,
"-p"
,
DB_STR
(
su_ctx
->
info
,
SU_MANAGER
),
"-p"
,
DB_STR
(
info
,
SU_MANAGER
),
"--user"
,
user
,
"--user"
,
user
,
"--es"
,
"action"
,
"request"
,
"--es"
,
"action"
,
"request"
,
"--es"
,
"socket"
,
(
char
*
)
socket
,
"--es"
,
"socket"
,
(
char
*
)
socket
,
...
@@ -102,8 +102,8 @@ void app_connect(const char *socket) {
...
@@ -102,8 +102,8 @@ void app_connect(const char *socket) {
silent_run
(
cmd
);
silent_run
(
cmd
);
}
}
void
socket_send_request
(
int
fd
)
{
void
socket_send_request
(
int
fd
,
struct
su_info
*
info
)
{
write_key_token
(
fd
,
"uid"
,
su_ctx
->
info
->
uid
);
write_key_token
(
fd
,
"uid"
,
info
->
uid
);
write_string_be
(
fd
,
"eof"
);
write_string_be
(
fd
,
"eof"
);
}
}
native/jni/su/su.c
View file @
d119dd9a
...
@@ -19,7 +19,6 @@
...
@@ -19,7 +19,6 @@
#include <errno.h>
#include <errno.h>
#include <signal.h>
#include <signal.h>
#include <sched.h>
#include <sched.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stat.h>
...
@@ -27,11 +26,9 @@
...
@@ -27,11 +26,9 @@
#include "daemon.h"
#include "daemon.h"
#include "utils.h"
#include "utils.h"
#include "su.h"
#include "su.h"
#include "
selinux
.h"
#include "
pts
.h"
#include "flags.h"
#include "flags.h"
struct
su_context
*
su_ctx
;
static
void
usage
(
int
status
)
{
static
void
usage
(
int
status
)
{
FILE
*
stream
=
(
status
==
EXIT_SUCCESS
)
?
stdout
:
stderr
;
FILE
*
stream
=
(
status
==
EXIT_SUCCESS
)
?
stdout
:
stderr
;
...
@@ -49,7 +46,7 @@ static void usage(int status) {
...
@@ -49,7 +46,7 @@ static void usage(int status) {
" -V display version code and exit
\n
"
" -V display version code and exit
\n
"
" -mm, -M,
\n
"
" -mm, -M,
\n
"
" --mount-master force run in the global mount namespace
\n
"
);
" --mount-master force run in the global mount namespace
\n
"
);
exit
2
(
status
);
exit
(
status
);
}
}
static
char
*
concat_commands
(
int
argc
,
char
*
argv
[])
{
static
char
*
concat_commands
(
int
argc
,
char
*
argv
[])
{
...
@@ -64,142 +61,103 @@ static char *concat_commands(int argc, char *argv[]) {
...
@@ -64,142 +61,103 @@ static char *concat_commands(int argc, char *argv[]) {
return
strdup
(
command
);
return
strdup
(
command
);
}
}
static
void
populate_environment
()
{
static
void
sighandler
(
int
sig
)
{
struct
passwd
*
pw
;
restore_stdin
();
if
(
su_ctx
->
to
.
keepenv
)
// Assume we'll only be called before death
return
;
// See note before sigaction() in set_stdin_raw()
//
pw
=
getpwuid
(
su_ctx
->
to
.
uid
);
// Now, close all standard I/O to cause the pumps
if
(
pw
)
{
// to exit so we can continue and retrieve the exit
setenv
(
"HOME"
,
pw
->
pw_dir
,
1
);
// code
if
(
su_ctx
->
to
.
shell
)
close
(
STDIN_FILENO
);
setenv
(
"SHELL"
,
su_ctx
->
to
.
shell
,
1
);
close
(
STDOUT_FILENO
);
else
close
(
STDERR_FILENO
);
setenv
(
"SHELL"
,
DEFAULT_SHELL
,
1
);
if
(
su_ctx
->
to
.
login
||
su_ctx
->
to
.
uid
)
{
// Put back all the default handlers
setenv
(
"USER"
,
pw
->
pw_name
,
1
);
struct
sigaction
act
;
setenv
(
"LOGNAME"
,
pw
->
pw_name
,
1
);
}
memset
(
&
act
,
0
,
sizeof
(
act
));
act
.
sa_handler
=
SIG_DFL
;
for
(
int
i
=
0
;
quit_signals
[
i
];
++
i
)
{
sigaction
(
quit_signals
[
i
],
&
act
,
NULL
);
}
}
}
}
void
set_identity
(
unsigned
uid
)
{
/*
/*
* Connect daemon, send argc, argv, cwd, pts slave
* Set effective uid back to root, otherwise setres[ug]id will fail
*/
* if uid isn't root.
int
su_client_main
(
int
argc
,
char
*
argv
[])
{
*/
int
c
;
if
(
seteuid
(
0
))
{
struct
option
long_opts
[]
=
{
PLOGE
(
"seteuid (root)"
);
{
"command"
,
required_argument
,
NULL
,
'c'
},
}
{
"help"
,
no_argument
,
NULL
,
'h'
},
if
(
setresgid
(
uid
,
uid
,
uid
))
{
{
"login"
,
no_argument
,
NULL
,
'l'
},
PLOGE
(
"setresgid (%u)"
,
uid
);
{
"preserve-environment"
,
no_argument
,
NULL
,
'p'
},
}
{
"shell"
,
required_argument
,
NULL
,
's'
},
if
(
setresuid
(
uid
,
uid
,
uid
))
{
{
"version"
,
no_argument
,
NULL
,
'v'
},
PLOGE
(
"setresuid (%u)"
,
uid
);
{
"context"
,
required_argument
,
NULL
,
'z'
},
}
{
"mount-master"
,
no_argument
,
NULL
,
'M'
},
}
{
NULL
,
0
,
NULL
,
0
},
};
static
__attribute__
((
noreturn
))
void
allow
()
{
char
*
argv
[]
=
{
NULL
,
NULL
,
NULL
,
NULL
};
if
(
su_ctx
->
to
.
login
)
argv
[
0
]
=
"-"
;
else
argv
[
0
]
=
basename
(
su_ctx
->
to
.
shell
);
if
(
su_ctx
->
to
.
command
)
{
argv
[
1
]
=
"-c"
;
argv
[
2
]
=
su_ctx
->
to
.
command
;
}
// Setup shell
umask
(
022
);
populate_environment
();
set_identity
(
su_ctx
->
to
.
uid
);
if
(
su_ctx
->
info
->
access
.
notify
||
su_ctx
->
info
->
access
.
log
)
app_log
();
execvp
(
su_ctx
->
to
.
shell
,
argv
);
fprintf
(
stderr
,
"Cannot execute %s: %s
\n
"
,
su_ctx
->
to
.
shell
,
strerror
(
errno
));
PLOGE
(
"exec"
);
exit
(
EXIT_FAILURE
);
}
static
__attribute__
((
noreturn
))
void
deny
()
{
struct
su_request
su_req
=
{
if
(
su_ctx
->
info
->
access
.
notify
||
su_ctx
->
info
->
access
.
log
)
.
uid
=
UID_ROOT
,
app_log
();
.
login
=
0
,
.
keepenv
=
0
,
.
shell
=
DEFAULT_SHELL
,
.
command
=
""
,
};
LOGW
(
"su: request rejected (%u->%u)"
,
su_ctx
->
info
->
uid
,
su_ctx
->
to
.
uid
);
fprintf
(
stderr
,
"%s
\n
"
,
strerror
(
EACCES
));
exit
(
EXIT_FAILURE
);
}
__attribute__
((
noreturn
))
void
exit2
(
int
status
)
{
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
{
// Handle the pipe, or the daemon will get stuck
// Replace -cn with -z, -mm with -M for supporting getopt_long
if
(
su_ctx
->
pipefd
[
0
]
>=
0
)
{
if
(
strcmp
(
argv
[
i
],
"-cn"
)
==
0
)
xwrite
(
su_ctx
->
pipefd
[
1
],
&
su_ctx
->
info
->
access
.
policy
,
sizeof
(
policy_t
)
);
strcpy
(
argv
[
i
],
"-z"
);
close
(
su_ctx
->
pipefd
[
0
]);
else
if
(
strcmp
(
argv
[
i
],
"-mm"
)
==
0
)
close
(
su_ctx
->
pipefd
[
1
]
);
strcpy
(
argv
[
i
],
"-M"
);
}
}
exit
(
status
);
}
int
su_daemon_main
(
int
argc
,
char
**
argv
)
{
int
c
;
struct
option
long_opts
[]
=
{
{
"command"
,
required_argument
,
NULL
,
'c'
},
{
"help"
,
no_argument
,
NULL
,
'h'
},
{
"login"
,
no_argument
,
NULL
,
'l'
},
{
"preserve-environment"
,
no_argument
,
NULL
,
'p'
},
{
"shell"
,
required_argument
,
NULL
,
's'
},
{
"version"
,
no_argument
,
NULL
,
'v'
},
{
"context"
,
required_argument
,
NULL
,
'z'
},
{
"mount-master"
,
no_argument
,
NULL
,
'M'
},
{
NULL
,
0
,
NULL
,
0
},
};
while
((
c
=
getopt_long
(
argc
,
argv
,
"c:hlmps:Vvuz:M"
,
long_opts
,
NULL
))
!=
-
1
)
{
while
((
c
=
getopt_long
(
argc
,
argv
,
"c:hlmps:Vvuz:M"
,
long_opts
,
NULL
))
!=
-
1
)
{
switch
(
c
)
{
switch
(
c
)
{
case
'c'
:
case
'c'
:
su_ctx
->
to
.
command
=
concat_commands
(
argc
,
argv
);
su_req
.
command
=
concat_commands
(
argc
,
argv
);
optind
=
argc
;
optind
=
argc
;
break
;
break
;
case
'h'
:
case
'h'
:
usage
(
EXIT_SUCCESS
);
usage
(
EXIT_SUCCESS
);
break
;
break
;
case
'l'
:
case
'l'
:
su_ctx
->
to
.
login
=
1
;
su_req
.
login
=
1
;
break
;
break
;
case
'm'
:
case
'm'
:
case
'p'
:
case
'p'
:
su_ctx
->
to
.
keepenv
=
1
;
su_req
.
keepenv
=
1
;
break
;
break
;
case
's'
:
case
's'
:
su_ctx
->
to
.
shell
=
optarg
;
su_req
.
shell
=
optarg
;
break
;
break
;
case
'V'
:
case
'V'
:
printf
(
"%d
\n
"
,
MAGISK_VER_CODE
);
printf
(
"%d
\n
"
,
MAGISK_VER_CODE
);
exit2
(
EXIT_SUCCESS
);
exit
(
EXIT_SUCCESS
);
case
'v'
:
case
'v'
:
printf
(
"%s
\n
"
,
xstr
(
MAGISK_VERSION
)
":MAGISKSU (topjohnwu)"
);
printf
(
"%s
\n
"
,
xstr
(
MAGISK_VERSION
)
":MAGISKSU (topjohnwu)"
);
exit2
(
EXIT_SUCCESS
);
exit
(
EXIT_SUCCESS
);
case
'z'
:
case
'z'
:
// Do nothing, placed here for legacy support :)
// Do nothing, placed here for legacy support :)
break
;
break
;
case
'M'
:
case
'M'
:
DB_SET
(
su_ctx
->
info
,
SU_MNT_NS
)
=
NAMESPACE_MODE_GLOBAL
;
/* TODO */
break
;
break
;
default
:
default:
/* Bionic getopt_long doesn't terminate its error output by newline */
/* Bionic getopt_long doesn't terminate its error output by newline */
fprintf
(
stderr
,
"
\n
"
);
fprintf
(
stderr
,
"
\n
"
);
usage
(
2
);
usage
(
2
);
}
}
}
}
if
(
optind
<
argc
&&
strcmp
(
argv
[
optind
],
"-"
)
==
0
)
{
if
(
optind
<
argc
&&
strcmp
(
argv
[
optind
],
"-"
)
==
0
)
{
su_
ctx
->
to
.
login
=
1
;
su_
req
.
login
=
1
;
optind
++
;
optind
++
;
}
}
/* username or uid */
/* username or uid */
...
@@ -207,54 +165,69 @@ int su_daemon_main(int argc, char **argv) {
...
@@ -207,54 +165,69 @@ int su_daemon_main(int argc, char **argv) {
struct
passwd
*
pw
;
struct
passwd
*
pw
;
pw
=
getpwnam
(
argv
[
optind
]);
pw
=
getpwnam
(
argv
[
optind
]);
if
(
pw
)
if
(
pw
)
su_
ctx
->
to
.
uid
=
pw
->
pw_uid
;
su_
req
.
uid
=
pw
->
pw_uid
;
else
else
su_
ctx
->
to
.
uid
=
atoi
(
argv
[
optind
]);
su_
req
.
uid
=
atoi
(
argv
[
optind
]);
optind
++
;
optind
++
;
}
}
// Handle namespaces
char
pts_slave
[
PATH_MAX
];
switch
(
DB_SET
(
su_ctx
->
info
,
SU_MNT_NS
))
{
int
ptmx
,
fd
;
case
NAMESPACE_MODE_GLOBAL
:
LOGD
(
"su: use global namespace
\n
"
);
break
;
case
NAMESPACE_MODE_REQUESTER
:
LOGD
(
"su: use namespace of pid=[%d]
\n
"
,
su_ctx
->
pid
);
if
(
switch_mnt_ns
(
su_ctx
->
pid
))
{
LOGD
(
"su: setns failed, fallback to isolated
\n
"
);
xunshare
(
CLONE_NEWNS
);
}
break
;
case
NAMESPACE_MODE_ISOLATE
:
LOGD
(
"su: use new isolated namespace
\n
"
);
xunshare
(
CLONE_NEWNS
);
break
;
}
// C
hange directory to cwd
// C
onnect to client
chdir
(
su_ctx
->
cwd
);
fd
=
connect_daemon
(
);
if
(
su_ctx
->
pipefd
[
0
]
>=
0
)
{
// Tell the daemon we are su
// Create random socket
write_int
(
fd
,
SUPERUSER
);
struct
sockaddr_un
addr
;
int
sockfd
=
create_rand_socket
(
&
addr
);
// Connect Magisk Manager
// Send su_request
app_connect
(
addr
.
sun_path
+
1
);
xwrite
(
fd
,
&
su_req
,
3
*
sizeof
(
unsigned
));
int
fd
=
socket_accept
(
sockfd
,
60
);
write_string
(
fd
,
su_req
.
shell
);
write_string
(
fd
,
su_req
.
command
);
socket_send_request
(
fd
);
// Determine which one of our streams are attached to a TTY
su_ctx
->
info
->
access
.
policy
=
read_int_be
(
fd
);
int
atty
=
0
;
if
(
isatty
(
STDIN_FILENO
))
atty
|=
ATTY_IN
;
if
(
isatty
(
STDOUT_FILENO
))
atty
|=
ATTY_OUT
;
if
(
isatty
(
STDERR_FILENO
))
atty
|=
ATTY_ERR
;
close
(
fd
);
if
(
atty
)
{
close
(
sockfd
);
// We need a PTY. Get one.
ptmx
=
pts_open
(
pts_slave
,
sizeof
(
pts_slave
));
}
else
{
pts_slave
[
0
]
=
'\0'
;
}
// Report the policy to main daemon
// Send pts_slave
xwrite
(
su_ctx
->
pipefd
[
1
],
&
su_ctx
->
info
->
access
.
policy
,
sizeof
(
policy_t
));
write_string
(
fd
,
pts_slave
);
close
(
su_ctx
->
pipefd
[
0
]);
close
(
su_ctx
->
pipefd
[
1
]);
// Send stdin
send_fd
(
fd
,
(
atty
&
ATTY_IN
)
?
-
1
:
STDIN_FILENO
);
// Send stdout
send_fd
(
fd
,
(
atty
&
ATTY_OUT
)
?
-
1
:
STDOUT_FILENO
);
// Send stderr
send_fd
(
fd
,
(
atty
&
ATTY_ERR
)
?
-
1
:
STDERR_FILENO
);
// Wait for ack from daemon
if
(
read_int
(
fd
))
{
// Fast fail
fprintf
(
stderr
,
"%s
\n
"
,
strerror
(
EACCES
));
return
DENY
;
}
}
su_ctx
->
info
->
access
.
policy
==
ALLOW
?
allow
()
:
deny
();
if
(
atty
&
ATTY_IN
)
{
}
setup_sighandlers
(
sighandler
);
pump_stdin_async
(
ptmx
);
}
if
(
atty
&
ATTY_OUT
)
{
// Forward SIGWINCH
watch_sigwinch_async
(
STDOUT_FILENO
,
ptmx
);
pump_stdout_blocking
(
ptmx
);
}
// Get the exit code
int
code
=
read_int
(
fd
);
close
(
fd
);
return
code
;
}
\ No newline at end of file
native/jni/su/su.h
View file @
d119dd9a
...
@@ -12,6 +12,11 @@
...
@@ -12,6 +12,11 @@
#define DEFAULT_SHELL "/system/bin/sh"
#define DEFAULT_SHELL "/system/bin/sh"
// Constants for atty
#define ATTY_IN 1
#define ATTY_OUT 2
#define ATTY_ERR 4
struct
su_info
{
struct
su_info
{
unsigned
uid
;
/* Unique key to find su_info */
unsigned
uid
;
/* Unique key to find su_info */
pthread_mutex_t
lock
;
/* Internal lock */
pthread_mutex_t
lock
;
/* Internal lock */
...
@@ -33,32 +38,22 @@ struct su_info {
...
@@ -33,32 +38,22 @@ struct su_info {
struct
su_request
{
struct
su_request
{
unsigned
uid
;
unsigned
uid
;
int
login
;
unsigned
login
;
int
keepenv
;
unsigned
keepenv
;
char
*
shell
;
char
*
shell
;
char
*
command
;
char
*
command
;
};
};
struct
su_context
{
struct
su_context
{
struct
su_info
*
info
;
struct
su_info
*
info
;
struct
su_request
to
;
struct
su_request
req
;
pid_t
pid
;
pid_t
pid
;
char
cwd
[
PATH_MAX
];
int
pipefd
[
2
];
};
};
extern
struct
su_context
*
su_ctx
;
// su.c
int
su_daemon_main
(
int
argc
,
char
**
argv
);
__attribute__
((
noreturn
))
void
exit2
(
int
status
);
void
set_identity
(
unsigned
uid
);
// connect.c
// connect.c
void
app_log
();
void
app_log
(
struct
su_context
*
ctx
);
void
app_connect
(
const
char
*
socket
);
void
app_connect
(
const
char
*
socket
,
struct
su_info
*
info
);
void
socket_send_request
(
int
fd
);
void
socket_send_request
(
int
fd
,
struct
su_info
*
info
);
#endif
#endif
native/jni/su/su_daemon.c
View file @
d119dd9a
...
@@ -2,13 +2,14 @@
...
@@ -2,13 +2,14 @@
*/
*/
#define _GNU_SOURCE
#define _GNU_SOURCE
#include <limits.h>
#include <unistd.h>
#include <unistd.h>
#include <pthread.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdlib.h>
#include <fcntl.h>
#include <fcntl.h>
#include <string.h>
#include <string.h>
#include <signal.h>
#include <signal.h>
#include <pwd.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stat.h>
...
@@ -21,44 +22,16 @@
...
@@ -21,44 +22,16 @@
#include "pts.h"
#include "pts.h"
#include "selinux.h"
#include "selinux.h"
// Constants for the atty bitfield
#define ATTY_IN 1
#define ATTY_OUT 2
#define ATTY_ERR 4
#define TIMEOUT 3
#define TIMEOUT 3
#define LOCK_CACHE() pthread_mutex_lock(&cache_lock)
#define LOCK_CACHE() pthread_mutex_lock(&cache_lock)
#define LOCK_INFO() pthread_mutex_lock(&info->lock)
#define LOCK_INFO() pthread_mutex_lock(&info->lock)
#define UNLOCK_CACHE() pthread_mutex_unlock(&cache_lock)
#define UNLOCK_CACHE() pthread_mutex_unlock(&cache_lock)
#define UNLOCK_INFO() pthread_mutex_unlock(&
ctx.
info->lock)
#define UNLOCK_INFO() pthread_mutex_unlock(&info->lock)
static
pthread_mutex_t
cache_lock
=
PTHREAD_MUTEX_INITIALIZER
;
static
pthread_mutex_t
cache_lock
=
PTHREAD_MUTEX_INITIALIZER
;
static
struct
su_info
*
cache
;
static
struct
su_info
*
cache
;
static
void
sighandler
(
int
sig
)
{
restore_stdin
();
// Assume we'll only be called before death
// See note before sigaction() in set_stdin_raw()
//
// Now, close all standard I/O to cause the pumps
// to exit so we can continue and retrieve the exit
// code
close
(
STDIN_FILENO
);
close
(
STDOUT_FILENO
);
close
(
STDERR_FILENO
);
// Put back all the default handlers
struct
sigaction
act
;
memset
(
&
act
,
0
,
sizeof
(
act
));
act
.
sa_handler
=
SIG_DFL
;
for
(
int
i
=
0
;
quit_signals
[
i
];
++
i
)
{
sigaction
(
quit_signals
[
i
],
&
act
,
NULL
);
}
}
static
void
*
info_collector
(
void
*
node
)
{
static
void
*
info_collector
(
void
*
node
)
{
struct
su_info
*
info
=
node
;
struct
su_info
*
info
=
node
;
while
(
1
)
{
while
(
1
)
{
...
@@ -185,48 +158,133 @@ static struct su_info *get_su_info(unsigned uid) {
...
@@ -185,48 +158,133 @@ static struct su_info *get_su_info(unsigned uid) {
if
(
info
->
access
.
policy
==
QUERY
&&
DB_STR
(
info
,
SU_MANAGER
)[
0
]
==
'\0'
)
if
(
info
->
access
.
policy
==
QUERY
&&
DB_STR
(
info
,
SU_MANAGER
)[
0
]
==
'\0'
)
info
->
access
=
NO_SU_ACCESS
;
info
->
access
=
NO_SU_ACCESS
;
}
}
// If still not determined, ask manager
if
(
info
->
access
.
policy
==
QUERY
)
{
// Create random socket
struct
sockaddr_un
addr
;
int
sockfd
=
create_rand_socket
(
&
addr
);
// Connect manager
app_connect
(
addr
.
sun_path
+
1
,
info
);
int
fd
=
socket_accept
(
sockfd
,
60
);
socket_send_request
(
fd
,
info
);
int
ret
=
read_int_be
(
fd
);
info
->
access
.
policy
=
ret
<
0
?
DENY
:
ret
;
close
(
fd
);
close
(
sockfd
);
}
// Unlock
UNLOCK_INFO
();
return
info
;
return
info
;
}
}
static
void
su_executor
(
int
client
)
{
static
void
populate_environment
(
struct
su_request
*
req
)
{
LOGD
(
"su: executor started
\n
"
);
struct
passwd
*
pw
;
if
(
req
->
keepenv
)
return
;
pw
=
getpwuid
(
req
->
uid
);
if
(
pw
)
{
setenv
(
"HOME"
,
pw
->
pw_dir
,
1
);
if
(
req
->
shell
)
setenv
(
"SHELL"
,
req
->
shell
,
1
);
else
setenv
(
"SHELL"
,
DEFAULT_SHELL
,
1
);
if
(
req
->
login
||
req
->
uid
)
{
setenv
(
"USER"
,
pw
->
pw_name
,
1
);
setenv
(
"LOGNAME"
,
pw
->
pw_name
,
1
);
}
}
}
static
void
set_identity
(
unsigned
uid
)
{
/*
* Set effective uid back to root, otherwise setres[ug]id will fail
* if uid isn't root.
*/
if
(
seteuid
(
0
))
{
PLOGE
(
"seteuid (root)"
);
}
if
(
setresgid
(
uid
,
uid
,
uid
))
{
PLOGE
(
"setresgid (%u)"
,
uid
);
}
if
(
setresuid
(
uid
,
uid
,
uid
))
{
PLOGE
(
"setresuid (%u)"
,
uid
);
}
}
void
su_daemon_handler
(
int
client
,
struct
ucred
*
credential
)
{
LOGD
(
"su: request from client: %d
\n
"
,
client
);
struct
su_info
*
info
=
get_su_info
(
credential
->
uid
);
// Fail fast
if
(
info
->
access
.
policy
==
DENY
&&
!
info
->
access
.
log
&&
!
info
->
access
.
notify
)
{
write_int
(
client
,
DENY
);
return
;
}
/* Fork a new process, the child process will need to setsid,
* open a pseudo-terminal if needed, and will eventually run exec
* The parent process will wait for the result and
* send the return code back to our client
*/
int
child
=
xfork
();
if
(
child
)
{
// Decrement reference count
--
info
->
ref
;
// Wait result
LOGD
(
"su: waiting child: [%d]
\n
"
,
child
);
int
status
,
code
;
if
(
waitpid
(
child
,
&
status
,
0
)
>
0
)
code
=
WEXITSTATUS
(
status
);
else
code
=
-
1
;
LOGD
(
"su: return code: [%d]
\n
"
,
code
);
write
(
client
,
&
code
,
sizeof
(
code
));
close
(
client
);
return
;
}
LOGD
(
"su: fork handler
\n
"
);
struct
su_context
ctx
=
{
.
info
=
info
,
.
pid
=
credential
->
pid
};
// Become session leader
// Become session leader
xsetsid
();
xsetsid
();
// Migrate environment from client
// Migrate environment from client
char
path
[
32
],
buf
[
4096
];
char
path
[
32
],
buf
[
4096
];
snprintf
(
path
,
sizeof
(
path
),
"/proc/%d/cwd"
,
su_ctx
->
pid
);
snprintf
(
path
,
sizeof
(
path
),
"/proc/%d/cwd"
,
ctx
.
pid
);
xreadlink
(
path
,
su_ctx
->
cwd
,
sizeof
(
su_ctx
->
cwd
));
xreadlink
(
path
,
buf
,
sizeof
(
buf
));
snprintf
(
path
,
sizeof
(
path
),
"/proc/%d/environ"
,
su_ctx
->
pid
);
chdir
(
buf
);
snprintf
(
path
,
sizeof
(
path
),
"/proc/%d/environ"
,
ctx
.
pid
);
memset
(
buf
,
0
,
sizeof
(
buf
));
memset
(
buf
,
0
,
sizeof
(
buf
));
int
fd
=
open
(
path
,
O_RDONLY
);
int
fd
=
open
(
path
,
O_RDONLY
);
read
(
fd
,
buf
,
sizeof
(
buf
));
read
(
fd
,
buf
,
sizeof
(
buf
));
close
(
fd
);
clearenv
();
clearenv
();
for
(
size_t
pos
=
0
;
buf
[
pos
];)
{
for
(
size_t
pos
=
0
;
buf
[
pos
];)
{
putenv
(
buf
+
pos
);
putenv
(
buf
+
pos
);
pos
+=
strlen
(
buf
+
pos
)
+
1
;
pos
+=
strlen
(
buf
+
pos
)
+
1
;
}
}
// Let's read some info from the socket
// Read su_request
int
argc
=
read_int
(
client
);
xxread
(
client
,
&
ctx
.
req
,
3
*
sizeof
(
unsigned
));
if
(
argc
<
0
||
argc
>
512
)
{
ctx
.
req
.
shell
=
read_string
(
client
);
LOGE
(
"unable to allocate args: %d"
,
argc
);
ctx
.
req
.
command
=
read_string
(
client
);
exit2
(
1
);
}
LOGD
(
"su: argc=[%d]
\n
"
,
argc
);
char
**
argv
=
(
char
**
)
xmalloc
(
sizeof
(
char
*
)
*
(
argc
+
1
));
argv
[
argc
]
=
NULL
;
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
{
argv
[
i
]
=
read_string
(
client
);
LOGD
(
"su: argv[%d]=[%s]
\n
"
,
i
,
argv
[
i
]);
// Replace -cn with -z, -mm with -M for supporting getopt_long
if
(
strcmp
(
argv
[
i
],
"-cn"
)
==
0
)
strcpy
(
argv
[
i
],
"-z"
);
else
if
(
strcmp
(
argv
[
i
],
"-mm"
)
==
0
)
strcpy
(
argv
[
i
],
"-M"
);
}
// Get pts_slave
// Get pts_slave
char
*
pts_slave
=
read_string
(
client
);
char
*
pts_slave
=
read_string
(
client
);
...
@@ -244,10 +302,10 @@ static void su_executor(int client) {
...
@@ -244,10 +302,10 @@ static void su_executor(int client) {
xstat
(
pts_slave
,
&
st
);
xstat
(
pts_slave
,
&
st
);
// If caller is not root, ensure the owner of pts_slave is the caller
// If caller is not root, ensure the owner of pts_slave is the caller
if
(
st
.
st_uid
!=
su_ctx
->
info
->
uid
&&
su_ctx
->
info
->
uid
!=
0
)
{
if
(
st
.
st_uid
!=
info
->
uid
&&
info
->
uid
!=
0
)
{
LOGE
(
"su: Wrong permission of pts_slave"
);
LOGE
(
"su: Wrong permission of pts_slave"
);
su_ctx
->
info
->
access
.
policy
=
DENY
;
info
->
access
.
policy
=
DENY
;
exit
2
(
1
);
exit
(
1
);
}
}
// Set our pts_slave to devpts, same restriction as adb shell
// Set our pts_slave to devpts, same restriction as adb shell
...
@@ -258,18 +316,12 @@ static void su_executor(int client) {
...
@@ -258,18 +316,12 @@ static void su_executor(int client) {
// our controlling TTY and not the daemon's
// our controlling TTY and not the daemon's
ptsfd
=
xopen
(
pts_slave
,
O_RDWR
);
ptsfd
=
xopen
(
pts_slave
,
O_RDWR
);
if
(
infd
<
0
)
{
if
(
infd
<
0
)
LOGD
(
"su: stdin using PTY"
);
infd
=
ptsfd
;
infd
=
ptsfd
;
if
(
outfd
<
0
)
}
if
(
outfd
<
0
)
{
LOGD
(
"su: stdout using PTY"
);
outfd
=
ptsfd
;
outfd
=
ptsfd
;
}
if
(
errfd
<
0
)
if
(
errfd
<
0
)
{
LOGD
(
"su: stderr using PTY"
);
errfd
=
ptsfd
;
errfd
=
ptsfd
;
}
}
}
free
(
pts_slave
);
free
(
pts_slave
);
...
@@ -285,142 +337,56 @@ static void su_executor(int client) {
...
@@ -285,142 +337,56 @@ static void su_executor(int client) {
write_int
(
client
,
0
);
write_int
(
client
,
0
);
close
(
client
);
close
(
client
);
// Run the actual main
// Handle namespaces
su_daemon_main
(
argc
,
argv
);
switch
(
DB_SET
(
info
,
SU_MNT_NS
))
{
}
case
NAMESPACE_MODE_GLOBAL
:
LOGD
(
"su: use global namespace
\n
"
);
void
su_daemon_receiver
(
int
client
,
struct
ucred
*
credential
)
{
break
;
LOGD
(
"su: request from client: %d
\n
"
,
client
);
case
NAMESPACE_MODE_REQUESTER
:
LOGD
(
"su: use namespace of pid=[%d]
\n
"
,
ctx
.
pid
);
// Default values
if
(
switch_mnt_ns
(
ctx
.
pid
))
{
struct
su_context
ctx
=
{
LOGD
(
"su: setns failed, fallback to isolated
\n
"
);
.
info
=
get_su_info
(
credential
->
uid
),
xunshare
(
CLONE_NEWNS
);
.
to
=
{
}
.
uid
=
UID_ROOT
,
break
;
.
login
=
0
,
case
NAMESPACE_MODE_ISOLATE
:
.
keepenv
=
0
,
LOGD
(
"su: use new isolated namespace
\n
"
);
.
shell
=
DEFAULT_SHELL
,
xunshare
(
CLONE_NEWNS
);
.
command
=
NULL
,
break
;
},
.
pid
=
credential
->
pid
,
.
pipefd
=
{
-
1
,
-
1
}
};
// Fail fast
if
(
ctx
.
info
->
access
.
policy
==
DENY
&&
!
ctx
.
info
->
access
.
log
&&
!
ctx
.
info
->
access
.
notify
)
{
UNLOCK_INFO
();
write_int
(
client
,
DENY
);
return
;
}
}
// If still not determined, open a pipe and wait for results
if
(
info
->
access
.
policy
)
{
if
(
ctx
.
info
->
access
.
policy
==
QUERY
)
char
*
argv
[]
=
{
NULL
,
NULL
,
NULL
,
NULL
};
xpipe2
(
ctx
.
pipefd
,
O_CLOEXEC
);
/* Fork a new process, the child process will need to setsid,
* open a pseudo-terminal if needed, and will eventually run exec
* The parent process will wait for the result and
* send the return code back to our client
*/
int
child
=
xfork
();
if
(
child
==
0
)
{
su_ctx
=
&
ctx
;
su_executor
(
client
);
}
// Wait for results
if
(
ctx
.
req
.
login
)
if
(
ctx
.
pipefd
[
0
]
>=
0
)
{
argv
[
0
]
=
"-"
;
xxread
(
ctx
.
pipefd
[
0
],
&
ctx
.
info
->
access
.
policy
,
sizeof
(
policy_t
));
else
close
(
ctx
.
pipefd
[
0
]);
argv
[
0
]
=
ctx
.
req
.
shell
;
close
(
ctx
.
pipefd
[
1
]);
}
// The policy is determined, unlock
if
(
ctx
.
req
.
command
[
0
])
{
UNLOCK_INFO
();
argv
[
1
]
=
"-c"
;
argv
[
2
]
=
ctx
.
req
.
command
;
// Info is now useless to us, decrement reference count
}
--
ctx
.
info
->
ref
;
// Wait result
LOGD
(
"su: waiting child: [%d]
\n
"
,
child
);
int
status
,
code
;
if
(
waitpid
(
child
,
&
status
,
0
)
>
0
)
code
=
WEXITSTATUS
(
status
);
else
code
=
-
1
;
LOGD
(
"su: return code: [%d]
\n
"
,
code
);
write
(
client
,
&
code
,
sizeof
(
code
));
close
(
client
);
return
;
}
/*
* Connect daemon, send argc, argv, cwd, pts slave
*/
int
su_client_main
(
int
argc
,
char
*
argv
[])
{
char
pts_slave
[
PATH_MAX
];
int
ptmx
,
socketfd
;
// Connect to client
socketfd
=
connect_daemon
();
// Tell the daemon we are su
write_int
(
socketfd
,
SUPERUSER
);
// Number of command line arguments
write_int
(
socketfd
,
argc
);
// Command line arguments
// Setup shell
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
{
umask
(
022
);
write_string
(
socketfd
,
argv
[
i
]
);
populate_environment
(
&
ctx
.
req
);
}
set_identity
(
ctx
.
req
.
uid
);
// Determine which one of our streams are attached to a TTY
if
(
info
->
access
.
notify
||
info
->
access
.
log
)
int
atty
=
0
;
app_log
(
&
ctx
);
if
(
isatty
(
STDIN_FILENO
))
atty
|=
ATTY_IN
;
if
(
isatty
(
STDOUT_FILENO
))
atty
|=
ATTY_OUT
;
if
(
isatty
(
STDERR_FILENO
))
atty
|=
ATTY_ERR
;
if
(
atty
)
{
execvp
(
ctx
.
req
.
shell
,
argv
);
// We need a PTY. Get one.
fprintf
(
stderr
,
"Cannot execute %s: %s
\n
"
,
ctx
.
req
.
shell
,
strerror
(
errno
));
ptmx
=
pts_open
(
pts_slave
,
sizeof
(
pts_slave
));
PLOGE
(
"exec"
);
exit
(
EXIT_FAILURE
);
}
else
{
}
else
{
pts_slave
[
0
]
=
'\0'
;
if
(
info
->
access
.
notify
||
info
->
access
.
log
)
}
app_log
(
&
ctx
);
// Send the pts_slave path to the daemon
LOGW
(
"su: request rejected (%u->%u)"
,
info
->
uid
,
ctx
.
req
.
uid
);
write_string
(
socketfd
,
pts_slave
);
// Send stdin
send_fd
(
socketfd
,
(
atty
&
ATTY_IN
)
?
-
1
:
STDIN_FILENO
);
// Send stdout
send_fd
(
socketfd
,
(
atty
&
ATTY_OUT
)
?
-
1
:
STDOUT_FILENO
);
// Send stderr
send_fd
(
socketfd
,
(
atty
&
ATTY_ERR
)
?
-
1
:
STDERR_FILENO
);
// Wait for acknowledgement from daemon
if
(
read_int
(
socketfd
))
{
// Fast fail
fprintf
(
stderr
,
"%s
\n
"
,
strerror
(
EACCES
));
fprintf
(
stderr
,
"%s
\n
"
,
strerror
(
EACCES
));
return
DENY
;
exit
(
EXIT_FAILURE
)
;
}
}
if
(
atty
&
ATTY_IN
)
{
setup_sighandlers
(
sighandler
);
pump_stdin_async
(
ptmx
);
}
if
(
atty
&
ATTY_OUT
)
{
// Forward SIGWINCH
watch_sigwinch_async
(
STDOUT_FILENO
,
ptmx
);
pump_stdout_blocking
(
ptmx
);
}
// Get the exit code
int
code
=
read_int
(
socketfd
);
close
(
socketfd
);
return
code
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment