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) {
ls_hide_list
(
client
);
break
;
case
SUPERUSER
:
su_daemon_
receiv
er
(
client
,
&
credential
);
su_daemon_
handl
er
(
client
,
&
credential
);
break
;
case
CHECK_VERSION
:
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) {
return
fd
;
}
int
socket_accept
(
int
s
erv_
fd
,
int
timeout
)
{
int
socket_accept
(
int
s
ock
fd
,
int
timeout
)
{
struct
timeval
tv
;
fd_set
fds
;
int
rc
;
...
...
@@ -47,16 +47,16 @@ int socket_accept(int serv_fd, int timeout) {
tv
.
tv_sec
=
timeout
;
tv
.
tv_usec
=
0
;
FD_ZERO
(
&
fds
);
FD_SET
(
s
erv_
fd
,
&
fds
);
FD_SET
(
s
ock
fd
,
&
fds
);
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
);
if
(
rc
<
1
)
{
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) {
int
read_int
(
int
fd
)
{
int
val
;
xxread
(
fd
,
&
val
,
sizeof
(
int
));
if
(
xxread
(
fd
,
&
val
,
sizeof
(
int
))
!=
sizeof
(
int
))
return
-
1
;
return
val
;
}
int
read_int_be
(
int
fd
)
{
uint32_t
val
;
xxread
(
fd
,
&
val
,
sizeof
(
val
));
if
(
xxread
(
fd
,
&
val
,
sizeof
(
val
))
!=
sizeof
(
int
))
return
-
1
;
return
ntohl
(
val
);
}
...
...
native/jni/include/daemon.h
View file @
d119dd9a
...
...
@@ -61,7 +61,7 @@ int check_and_start_logger();
socklen_t
setup_sockaddr
(
struct
sockaddr_un
*
sun
,
daemon_t
d
);
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
);
void
send_fd
(
int
sockfd
,
int
fd
);
int
read_int
(
int
fd
);
...
...
@@ -98,6 +98,6 @@ void ls_hide_list(int client);
* Superuser *
*************/
void
su_daemon_
receiv
er
(
int
client
,
struct
ucred
*
credential
);
void
su_daemon_
handl
er
(
int
client
,
struct
ucred
*
credential
);
#endif
native/jni/su/connect.c
View file @
d119dd9a
...
...
@@ -14,86 +14,86 @@
#include "magisk.h"
#include "daemon.h"
#include "utils.h"
#include "su.h"
#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am"
static
char
*
get_command
(
const
struct
su_request
*
to
)
{
if
(
to
->
command
)
if
(
to
->
command
[
0
]
)
return
to
->
command
;
if
(
to
->
shell
)
if
(
to
->
shell
[
0
]
)
return
to
->
shell
;
return
DEFAULT_SHELL
;
}
static
void
silent_run
(
char
*
const
args
[])
{
set_identity
(
0
);
if
(
fork
())
if
(
fork_dont_care
())
return
;
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
);
dup2
(
null
,
1
);
dup2
(
null
,
2
);
x
dup2
(
null
,
1
);
x
dup2
(
null
,
2
);
setenv
(
"CLASSPATH"
,
"/system/framework/am.jar"
,
1
);
execv
(
args
[
0
],
args
);
PLOGE
(
"exec am"
);
_exit
(
EXIT_FAILURE
);
}
static
void
setup_user
(
char
*
user
)
{
switch
(
DB_SET
(
su_ctx
->
info
,
SU_MULTIUSER_MODE
))
{
static
void
setup_user
(
char
*
user
,
struct
su_info
*
info
)
{
switch
(
DB_SET
(
info
,
SU_MULTIUSER_MODE
))
{
case
MULTIUSER_MODE_OWNER_ONLY
:
case
MULTIUSER_MODE_OWNER_MANAGED
:
sprintf
(
user
,
"%d"
,
0
);
break
;
case
MULTIUSER_MODE_USER
:
sprintf
(
user
,
"%d"
,
su_ctx
->
info
->
uid
/
100000
);
sprintf
(
user
,
"%d"
,
info
->
uid
/
100000
);
break
;
}
}
void
app_log
()
{
void
app_log
(
struct
su_context
*
ctx
)
{
char
user
[
8
];
setup_user
(
user
);
setup_user
(
user
,
ctx
->
info
);
char
fromUid
[
8
];
sprintf
(
fromUid
,
"%d"
,
DB_SET
(
su_
ctx
->
info
,
SU_MULTIUSER_MODE
)
==
MULTIUSER_MODE_OWNER_MANAGED
?
su_ctx
->
info
->
uid
%
100000
:
su_
ctx
->
info
->
uid
);
DB_SET
(
ctx
->
info
,
SU_MULTIUSER_MODE
)
==
MULTIUSER_MODE_OWNER_MANAGED
?
ctx
->
info
->
uid
%
100000
:
ctx
->
info
->
uid
);
char
toUid
[
8
];
sprintf
(
toUid
,
"%d"
,
su_ctx
->
to
.
uid
);
sprintf
(
toUid
,
"%d"
,
ctx
->
req
.
uid
);
char
pid
[
8
];
sprintf
(
pid
,
"%d"
,
su_
ctx
->
pid
);
sprintf
(
pid
,
"%d"
,
ctx
->
pid
);
char
policy
[
2
];
sprintf
(
policy
,
"%d"
,
su_
ctx
->
info
->
access
.
policy
);
sprintf
(
policy
,
"%d"
,
ctx
->
info
->
access
.
policy
);
char
*
cmd
[]
=
{
AM_PATH
,
"broadcast"
,
"-a"
,
"android.intent.action.BOOT_COMPLETED"
,
"-p"
,
DB_STR
(
su_
ctx
->
info
,
SU_MANAGER
),
"-p"
,
DB_STR
(
ctx
->
info
,
SU_MANAGER
),
"--user"
,
user
,
"--es"
,
"action"
,
"log"
,
"--ei"
,
"from.uid"
,
fromUid
,
"--ei"
,
"to.uid"
,
toUid
,
"--ei"
,
"pid"
,
pid
,
"--ei"
,
"policy"
,
policy
,
"--es"
,
"command"
,
get_command
(
&
su_ctx
->
to
),
"--es"
,
"command"
,
get_command
(
&
ctx
->
req
),
NULL
};
silent_run
(
cmd
);
}
void
app_connect
(
const
char
*
socket
)
{
void
app_connect
(
const
char
*
socket
,
struct
su_info
*
info
)
{
char
user
[
8
];
setup_user
(
user
);
setup_user
(
user
,
info
);
char
*
cmd
[]
=
{
AM_PATH
,
"broadcast"
,
"-a"
,
"android.intent.action.BOOT_COMPLETED"
,
"-p"
,
DB_STR
(
su_ctx
->
info
,
SU_MANAGER
),
"-p"
,
DB_STR
(
info
,
SU_MANAGER
),
"--user"
,
user
,
"--es"
,
"action"
,
"request"
,
"--es"
,
"socket"
,
(
char
*
)
socket
,
...
...
@@ -102,8 +102,8 @@ void app_connect(const char *socket) {
silent_run
(
cmd
);
}
void
socket_send_request
(
int
fd
)
{
write_key_token
(
fd
,
"uid"
,
su_ctx
->
info
->
uid
);
void
socket_send_request
(
int
fd
,
struct
su_info
*
info
)
{
write_key_token
(
fd
,
"uid"
,
info
->
uid
);
write_string_be
(
fd
,
"eof"
);
}
native/jni/su/su.c
View file @
d119dd9a
...
...
@@ -19,7 +19,6 @@
#include <errno.h>
#include <signal.h>
#include <sched.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
...
...
@@ -27,11 +26,9 @@
#include "daemon.h"
#include "utils.h"
#include "su.h"
#include "
selinux
.h"
#include "
pts
.h"
#include "flags.h"
struct
su_context
*
su_ctx
;
static
void
usage
(
int
status
)
{
FILE
*
stream
=
(
status
==
EXIT_SUCCESS
)
?
stdout
:
stderr
;
...
...
@@ -49,7 +46,7 @@ static void usage(int status) {
" -V display version code and exit
\n
"
" -mm, -M,
\n
"
" --mount-master force run in the global mount namespace
\n
"
);
exit
2
(
status
);
exit
(
status
);
}
static
char
*
concat_commands
(
int
argc
,
char
*
argv
[])
{
...
...
@@ -64,142 +61,103 @@ static char *concat_commands(int argc, char *argv[]) {
return
strdup
(
command
);
}
static
void
populate_environment
()
{
struct
passwd
*
pw
;
if
(
su_ctx
->
to
.
keepenv
)
return
;
pw
=
getpwuid
(
su_ctx
->
to
.
uid
);
if
(
pw
)
{
setenv
(
"HOME"
,
pw
->
pw_dir
,
1
);
if
(
su_ctx
->
to
.
shell
)
setenv
(
"SHELL"
,
su_ctx
->
to
.
shell
,
1
);
else
setenv
(
"SHELL"
,
DEFAULT_SHELL
,
1
);
if
(
su_ctx
->
to
.
login
||
su_ctx
->
to
.
uid
)
{
setenv
(
"USER"
,
pw
->
pw_name
,
1
);
setenv
(
"LOGNAME"
,
pw
->
pw_name
,
1
);
}
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
);
}
}
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
);
}
}
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
);
}
/*
* Connect daemon, send argc, argv, cwd, pts slave
*/
int
su_client_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
},
};
static
__attribute__
((
noreturn
))
void
deny
()
{
if
(
su_ctx
->
info
->
access
.
notify
||
su_ctx
->
info
->
access
.
log
)
app_log
();
struct
su_request
su_req
=
{
.
uid
=
UID_ROOT
,
.
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
)
{
// Handle the pipe, or the daemon will get stuck
if
(
su_ctx
->
pipefd
[
0
]
>=
0
)
{
xwrite
(
su_ctx
->
pipefd
[
1
],
&
su_ctx
->
info
->
access
.
policy
,
sizeof
(
policy_t
)
);
close
(
su_ctx
->
pipefd
[
0
]);
close
(
su_ctx
->
pipefd
[
1
]
);
for
(
int
i
=
0
;
i
<
argc
;
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"
);
}
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
)
{
switch
(
c
)
{
case
'c'
:
su_ctx
->
to
.
command
=
concat_commands
(
argc
,
argv
);
optind
=
argc
;
break
;
case
'h'
:
usage
(
EXIT_SUCCESS
);
break
;
case
'l'
:
su_ctx
->
to
.
login
=
1
;
break
;
case
'm'
:
case
'p'
:
su_ctx
->
to
.
keepenv
=
1
;
break
;
case
's'
:
su_ctx
->
to
.
shell
=
optarg
;
break
;
case
'V'
:
printf
(
"%d
\n
"
,
MAGISK_VER_CODE
);
exit2
(
EXIT_SUCCESS
);
case
'v'
:
printf
(
"%s
\n
"
,
xstr
(
MAGISK_VERSION
)
":MAGISKSU (topjohnwu)"
);
exit2
(
EXIT_SUCCESS
);
case
'z'
:
// Do nothing, placed here for legacy support :)
break
;
case
'M'
:
DB_SET
(
su_ctx
->
info
,
SU_MNT_NS
)
=
NAMESPACE_MODE_GLOBAL
;
break
;
default
:
/* Bionic getopt_long doesn't terminate its error output by newline */
fprintf
(
stderr
,
"
\n
"
);
usage
(
2
);
case
'c'
:
su_req
.
command
=
concat_commands
(
argc
,
argv
);
optind
=
argc
;
break
;
case
'h'
:
usage
(
EXIT_SUCCESS
);
break
;
case
'l'
:
su_req
.
login
=
1
;
break
;
case
'm'
:
case
'p'
:
su_req
.
keepenv
=
1
;
break
;
case
's'
:
su_req
.
shell
=
optarg
;
break
;
case
'V'
:
printf
(
"%d
\n
"
,
MAGISK_VER_CODE
);
exit
(
EXIT_SUCCESS
);
case
'v'
:
printf
(
"%s
\n
"
,
xstr
(
MAGISK_VERSION
)
":MAGISKSU (topjohnwu)"
);
exit
(
EXIT_SUCCESS
);
case
'z'
:
// Do nothing, placed here for legacy support :)
break
;
case
'M'
:
/* TODO */
break
;
default:
/* Bionic getopt_long doesn't terminate its error output by newline */
fprintf
(
stderr
,
"
\n
"
);
usage
(
2
);
}
}
if
(
optind
<
argc
&&
strcmp
(
argv
[
optind
],
"-"
)
==
0
)
{
su_
ctx
->
to
.
login
=
1
;
su_
req
.
login
=
1
;
optind
++
;
}
/* username or uid */
...
...
@@ -207,54 +165,69 @@ int su_daemon_main(int argc, char **argv) {
struct
passwd
*
pw
;
pw
=
getpwnam
(
argv
[
optind
]);
if
(
pw
)
su_
ctx
->
to
.
uid
=
pw
->
pw_uid
;
su_
req
.
uid
=
pw
->
pw_uid
;
else
su_
ctx
->
to
.
uid
=
atoi
(
argv
[
optind
]);
su_
req
.
uid
=
atoi
(
argv
[
optind
]);
optind
++
;
}
// Handle namespaces
switch
(
DB_SET
(
su_ctx
->
info
,
SU_MNT_NS
))
{
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
;
}
char
pts_slave
[
PATH_MAX
];
int
ptmx
,
fd
;
// C
hange directory to cwd
chdir
(
su_ctx
->
cwd
);
// C
onnect to client
fd
=
connect_daemon
(
);
if
(
su_ctx
->
pipefd
[
0
]
>=
0
)
{
// Create random socket
struct
sockaddr_un
addr
;
int
sockfd
=
create_rand_socket
(
&
addr
);
// Tell the daemon we are su
write_int
(
fd
,
SUPERUSER
);
// Connect Magisk Manager
app_connect
(
addr
.
sun_path
+
1
);
int
fd
=
socket_accept
(
sockfd
,
60
);
// Send su_request
xwrite
(
fd
,
&
su_req
,
3
*
sizeof
(
unsigned
));
write_string
(
fd
,
su_req
.
shell
);
write_string
(
fd
,
su_req
.
command
);
socket_send_request
(
fd
);
su_ctx
->
info
->
access
.
policy
=
read_int_be
(
fd
);
// Determine which one of our streams are attached to a TTY
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
);
close
(
sockfd
);
if
(
atty
)
{
// 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
xwrite
(
su_ctx
->
pipefd
[
1
],
&
su_ctx
->
info
->
access
.
policy
,
sizeof
(
policy_t
));
close
(
su_ctx
->
pipefd
[
0
]);
close
(
su_ctx
->
pipefd
[
1
]);
// Send pts_slave
write_string
(
fd
,
pts_slave
);
// 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 @@
#define DEFAULT_SHELL "/system/bin/sh"
// Constants for atty
#define ATTY_IN 1
#define ATTY_OUT 2
#define ATTY_ERR 4
struct
su_info
{
unsigned
uid
;
/* Unique key to find su_info */
pthread_mutex_t
lock
;
/* Internal lock */
...
...
@@ -33,32 +38,22 @@ struct su_info {
struct
su_request
{
unsigned
uid
;
int
login
;
int
keepenv
;
unsigned
login
;
unsigned
keepenv
;
char
*
shell
;
char
*
command
;
};
struct
su_context
{
struct
su_info
*
info
;
struct
su_request
to
;
struct
su_request
req
;
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
void
app_log
();
void
app_connect
(
const
char
*
socket
);
void
socket_send_request
(
int
fd
);
void
app_log
(
struct
su_context
*
ctx
);
void
app_connect
(
const
char
*
socket
,
struct
su_info
*
info
);
void
socket_send_request
(
int
fd
,
struct
su_info
*
info
);
#endif
native/jni/su/su_daemon.c
View file @
d119dd9a
...
...
@@ -2,13 +2,14 @@
*/
#define _GNU_SOURCE
#include <limits.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <pwd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
...
...
@@ -21,44 +22,16 @@
#include "pts.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 LOCK_CACHE() pthread_mutex_lock(&cache_lock)
#define LOCK_INFO() pthread_mutex_lock(&info->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
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
)
{
struct
su_info
*
info
=
node
;
while
(
1
)
{
...
...
@@ -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'
)
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
;
}
static
void
su_executor
(
int
client
)
{
LOGD
(
"su: executor started
\n
"
);
static
void
populate_environment
(
struct
su_request
*
req
)
{
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
xsetsid
();
// Migrate environment from client
char
path
[
32
],
buf
[
4096
];
snprintf
(
path
,
sizeof
(
path
),
"/proc/%d/cwd"
,
su_ctx
->
pid
);
xreadlink
(
path
,
su_ctx
->
cwd
,
sizeof
(
su_ctx
->
cwd
));
snprintf
(
path
,
sizeof
(
path
),
"/proc/%d/environ"
,
su_ctx
->
pid
);
snprintf
(
path
,
sizeof
(
path
),
"/proc/%d/cwd"
,
ctx
.
pid
);
xreadlink
(
path
,
buf
,
sizeof
(
buf
));
chdir
(
buf
);
snprintf
(
path
,
sizeof
(
path
),
"/proc/%d/environ"
,
ctx
.
pid
);
memset
(
buf
,
0
,
sizeof
(
buf
));
int
fd
=
open
(
path
,
O_RDONLY
);
read
(
fd
,
buf
,
sizeof
(
buf
));
close
(
fd
);
clearenv
();
for
(
size_t
pos
=
0
;
buf
[
pos
];)
{
putenv
(
buf
+
pos
);
pos
+=
strlen
(
buf
+
pos
)
+
1
;
}
// Let's read some info from the socket
int
argc
=
read_int
(
client
);
if
(
argc
<
0
||
argc
>
512
)
{
LOGE
(
"unable to allocate args: %d"
,
argc
);
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"
);
}
// Read su_request
xxread
(
client
,
&
ctx
.
req
,
3
*
sizeof
(
unsigned
));
ctx
.
req
.
shell
=
read_string
(
client
);
ctx
.
req
.
command
=
read_string
(
client
);
// Get pts_slave
char
*
pts_slave
=
read_string
(
client
);
...
...
@@ -244,10 +302,10 @@ static void su_executor(int client) {
xstat
(
pts_slave
,
&
st
);
// 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"
);
su_ctx
->
info
->
access
.
policy
=
DENY
;
exit
2
(
1
);
info
->
access
.
policy
=
DENY
;
exit
(
1
);
}
// Set our pts_slave to devpts, same restriction as adb shell
...
...
@@ -258,18 +316,12 @@ static void su_executor(int client) {
// our controlling TTY and not the daemon's
ptsfd
=
xopen
(
pts_slave
,
O_RDWR
);
if
(
infd
<
0
)
{
LOGD
(
"su: stdin using PTY"
);
infd
=
ptsfd
;
}
if
(
outfd
<
0
)
{
LOGD
(
"su: stdout using PTY"
);
if
(
infd
<
0
)
infd
=
ptsfd
;
if
(
outfd
<
0
)
outfd
=
ptsfd
;
}
if
(
errfd
<
0
)
{
LOGD
(
"su: stderr using PTY"
);
if
(
errfd
<
0
)
errfd
=
ptsfd
;
}
}
free
(
pts_slave
);
...
...
@@ -285,142 +337,56 @@ static void su_executor(int client) {
write_int
(
client
,
0
);
close
(
client
);
// Run the actual main
su_daemon_main
(
argc
,
argv
);
}
void
su_daemon_receiver
(
int
client
,
struct
ucred
*
credential
)
{
LOGD
(
"su: request from client: %d
\n
"
,
client
);
// Default values
struct
su_context
ctx
=
{
.
info
=
get_su_info
(
credential
->
uid
),
.
to
=
{
.
uid
=
UID_ROOT
,
.
login
=
0
,
.
keepenv
=
0
,
.
shell
=
DEFAULT_SHELL
,
.
command
=
NULL
,
},
.
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
;
// Handle namespaces
switch
(
DB_SET
(
info
,
SU_MNT_NS
))
{
case
NAMESPACE_MODE_GLOBAL
:
LOGD
(
"su: use global namespace
\n
"
);
break
;
case
NAMESPACE_MODE_REQUESTER
:
LOGD
(
"su: use namespace of pid=[%d]
\n
"
,
ctx
.
pid
);
if
(
switch_mnt_ns
(
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
;
}
// If still not determined, open a pipe and wait for results
if
(
ctx
.
info
->
access
.
policy
==
QUERY
)
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
);
}
if
(
info
->
access
.
policy
)
{
char
*
argv
[]
=
{
NULL
,
NULL
,
NULL
,
NULL
};
// Wait for results
if
(
ctx
.
pipefd
[
0
]
>=
0
)
{
xxread
(
ctx
.
pipefd
[
0
],
&
ctx
.
info
->
access
.
policy
,
sizeof
(
policy_t
));
close
(
ctx
.
pipefd
[
0
]);
close
(
ctx
.
pipefd
[
1
]);
}
if
(
ctx
.
req
.
login
)
argv
[
0
]
=
"-"
;
else
argv
[
0
]
=
ctx
.
req
.
shell
;
// The policy is determined, unlock
UNLOCK_INFO
();
// 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
);
if
(
ctx
.
req
.
command
[
0
])
{
argv
[
1
]
=
"-c"
;
argv
[
2
]
=
ctx
.
req
.
command
;
}
// Command line arguments
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
{
write_string
(
socketfd
,
argv
[
i
]
);
}
// Setup shell
umask
(
022
);
populate_environment
(
&
ctx
.
req
);
set_identity
(
ctx
.
req
.
uid
);
// Determine which one of our streams are attached to a TTY
int
atty
=
0
;
if
(
isatty
(
STDIN_FILENO
))
atty
|=
ATTY_IN
;
if
(
isatty
(
STDOUT_FILENO
))
atty
|=
ATTY_OUT
;
if
(
isatty
(
STDERR_FILENO
))
atty
|=
ATTY_ERR
;
if
(
info
->
access
.
notify
||
info
->
access
.
log
)
app_log
(
&
ctx
);
if
(
atty
)
{
// We need a PTY. Get one.
ptmx
=
pts_open
(
pts_slave
,
sizeof
(
pts_slave
));
execvp
(
ctx
.
req
.
shell
,
argv
);
fprintf
(
stderr
,
"Cannot execute %s: %s
\n
"
,
ctx
.
req
.
shell
,
strerror
(
errno
));
PLOGE
(
"exec"
);
exit
(
EXIT_FAILURE
);
}
else
{
pts_slave
[
0
]
=
'\0'
;
}
if
(
info
->
access
.
notify
||
info
->
access
.
log
)
app_log
(
&
ctx
);
// Send the pts_slave path to the daemon
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
LOGW
(
"su: request rejected (%u->%u)"
,
info
->
uid
,
ctx
.
req
.
uid
);
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