-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathsetuid_overview.html
261 lines (261 loc) · 15.4 KB
/
setuid_overview.html
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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>setuid_overview(7) - SerenityOS man pages</title>
<style>
html {
line-height: 1.5;
font-family: Georgia, serif;
font-size: 20px;
color: #1a1a1a;
background-color: #fdfdfd;
}
body {
margin: 0 auto;
max-width: 36em;
padding-left: 50px;
padding-right: 50px;
padding-top: 50px;
padding-bottom: 50px;
hyphens: auto;
word-wrap: break-word;
text-rendering: optimizeLegibility;
font-kerning: normal;
}
@media (max-width: 600px) {
body {
font-size: 0.9em;
padding: 1em;
}
}
@media print {
body {
background-color: transparent;
color: black;
font-size: 12pt;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3, h4 {
page-break-after: avoid;
}
}
p {
margin: 1em 0;
}
a {
color: #1a1a1a;
}
a:visited {
color: #1a1a1a;
}
img {
max-width: 100%;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1.4em;
}
h5, h6 {
font-size: 1em;
font-style: italic;
}
h6 {
font-weight: normal;
}
ol, ul {
padding-left: 1.7em;
margin-top: 1em;
}
li > ol, li > ul {
margin-top: 0;
}
blockquote {
margin: 1em 0 1em 1.7em;
padding-left: 1em;
border-left: 2px solid #e6e6e6;
color: #606060;
}
code {
font-family: Menlo, Monaco, 'Lucida Console', Consolas, monospace;
font-size: 85%;
margin: 0;
}
pre {
margin: 1em 0;
overflow: auto;
}
pre code {
padding: 0;
overflow: visible;
}
.sourceCode {
background-color: transparent;
overflow: visible;
}
hr {
background-color: #1a1a1a;
border: none;
height: 1px;
margin: 1em 0;
}
table {
margin: 1em 0;
border-collapse: collapse;
width: 100%;
overflow-x: auto;
display: block;
font-variant-numeric: lining-nums tabular-nums;
}
table caption {
margin-bottom: 0.75em;
}
tbody {
margin-top: 0.5em;
border-top: 1px solid #1a1a1a;
border-bottom: 1px solid #1a1a1a;
}
th {
border-top: 1px solid #1a1a1a;
padding: 0.25em 0.5em 0.25em 0.5em;
}
td {
padding: 0.125em 0.5em 0.25em 0.5em;
}
header {
margin-bottom: 4em;
text-align: center;
}
#TOC li {
list-style: none;
}
#TOC a:not(:hover) {
text-decoration: none;
}
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<img src="/banner.png" style="display: block; margin-left: auto; margin-right: auto; margin-bottom: 2em;">
<header id="title-block-header">
<h1 class="title">setuid_overview(7) - SerenityOS man pages</h1>
</header>
<h2 id="name"><a style="margin-right: 15px" href="#name">#</a>Name</h2>
<p>Overview of real, effective, and saved user and group IDs</p>
<h2 id="description"><a style="margin-right: 15px" href="#description">#</a>Description</h2>
<p>Each process runs has a user ID and a group ID. By default, a new process inherits the user ID and group ID of its parent process.</p>
<p>Each file has permissions that control if it can be read, written, and executed by processes belonging to its owner, by processes belonging to other users in the owner's group, and by processes belonging to others.</p>
<p>In addition to the access permissions bits, executables may have the "Set User ID" and the "Set Group ID" bit set. When executables with these bits set are executed, they run with the user or group ID of the executable, instead of with the user and group ID of their parent process. These binaries are also called "SUID binaries" or "setuid binaries" (or "SGID binaries" or "setgid binaries" for the "Set Group ID" bit).</p>
<p>The motivation behind SUID binaries is that they allow users to do tasks that would normally require elevated permissions, without having to give users these permissions fully.</p>
<p>For example, <code>/bin/ping</code> has the Set User ID bit set and is owned by user root in group root. So if any process executes <code>/bin/ping</code>, it will run as root, which means it will be able to send network packets, even if the current process doesn't normally have network access.</p>
<p>For another example, many other Unix systems contain a utility <code>passwd</code> that changes the password of the current user. To store the password, it has to write to <code>/etc/shadow</code> -- but the current user is not supposed to have write access to <code>/etc/shadow</code> so that they cannot change passwords of other users. The solution is to make <code>passwd</code> a SUID binary. (SerenityOS currently doesn't have support for user passwords.)</p>
<p>SUID binaries mean that the effective user ID of a process and the real user ID of a process can be different. When a SUID binary is executed, it assumes the owner of the binary as effective user ID, and the user ID of the parent process that executed it as real user ID. The effective user ID is used for permission checks.</p>
<p>Since SUID binaries are able to bypass access checks, only carefully selected binaries should be made SUID. If, for example, <code>cp</code> was SUID root, everyone could overwrite every file using this <code>cp</code> binary.</p>
<p>In some instances, it is useful for a SUID binary to either temporarily or permanently drop its permissions and set the effective user ID to the real user ID.</p>
<p>To make this possible, each process has <em>three</em> user (and group) IDs: The (real) user ID, the <em>effective</em> user ID, and the <em>saved</em> user ID. When a process executes a normal binary, all three IDs are set to the parent process's user ID. However, when a process executes a SUID binary, the process runs with the parent process's ID as its real ID, but it takes its effective ID and saved ID from the binary. (Analogously for the group ID for SGID binaries.)</p>
<p>The function <a href="/man2/getresuid.html"><code>setresuid</code>(2)</a> can change the real, effective, and saved user ID of a process -- but for non-root processes it is only valid to set each new ID to the current value of real, effective, or saved user ID. Since SUID binaries start with the binary's owner as effective and saved user ID and with the current user's ID as real user ID, this allows switching the effective user ID between the SUID owner's ID and the current user's ID.</p>
<p>Hence, to temporarily drop SUID privileges, set the effective ID to a less privileged user ID, and store the current effective user ID in the saved user ID so that it can be restored in a later call:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>setresuid<span class="op">(-</span><span class="dv">1</span><span class="op">,</span> new_uid<span class="op">,</span> geteuid<span class="op">())</span> <span class="op"><</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> OH_NO<span class="op">;</span></span></code></pre></div>
<p>(Since the saved ID starts out as the file owner's ID for SUID binaries, this should in practice be the same as <code>seteuid(getuid())</code>, but it's easier to reason about.)</p>
<p>The process then has fewer permissions, but since the former effective ID is still stored in the saved ID, the process can restore its former permissions with:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="dt">uid_t</span> ruid<span class="op">,</span> euid<span class="op">,</span> suid<span class="op">;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>getresuid<span class="op">(&</span>ruid<span class="op">,</span> <span class="op">&</span>euid<span class="op">,</span> <span class="op">&</span>suid<span class="op">)</span> <span class="op"><</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> OH_NO<span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>setresuid<span class="op">(-</span><span class="dv">1</span><span class="op">,</span> suid<span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">)</span> <span class="op"><</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> OH_NO<span class="op">;</span></span></code></pre></div>
<p>(Since SUID binaries are often owned by root who has user ID 0, this is often identical to <code>seteuid(0)</code> -- in particular, if a SUID root binary accepts user input in an unsafe way with temporarily dropped privileges, then if a user manages to take control of the binary with malicious input they can restore privileges with <code>seteuid(0)</code>.)</p>
<p>A process can permanently drop its SUID privileges by copying the real user ID into both effective and saved user ID. Then, it's impossible to set the effective ID to anything else (assuming the current user isn't the superuser). To permanently drop privileges:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>setresuid<span class="op">(</span>new_uid<span class="op">,</span> new_uid<span class="op">,</span> new_uid<span class="op">)</span> <span class="op"><</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> OH_NO<span class="op">;</span></span></code></pre></div>
<p>(On SerenityOS, this is usually the same as calling <code>setuid(new_uid)</code>, but easier to reason about.)</p>
<p>Changing group IDs is analogous. Since changing the user ID changes the permissions of a process, group privileges should be dropped before user privileges are dropped, and if they're dropped temporarily, user privileges should be restored before group privileges are restored.</p>
<p>For historical reasons, there are many functions for setting and getting these IDs. <code>setresuid()</code>, <code>setresgid()</code>, <code>getresuid()</code>, and <code>getresgid()</code> are the most flexible of these functions and they have the easiest to understand semantics.</p>
<h2 id="see-also"><a style="margin-right: 15px" href="#see-also">#</a>See also</h2>
<ul>
<li>"Setuid Demystified", Proceedings of the 11th USENIX Security Symposium, August 2002, Pages 171–190</li>
<li><a href="/man2/getresuid.html"><code>getresuid</code>(2) / <code>getresgid</code>(2)</a></li>
<li><a href="/man2/geteuid.html"><code>geteuid</code>(2) / <code>getegid</code>(2)</a></li>
<li><a href="/man2/getuid.html"><code>getuid</code>(2) / <code>getgid</code>(2)</a></li>
<li><a href="/man2/seteuid.html"><code>seteuid</code>(2) / <code>setegid</code>(2)</a></li>
<li><a href="/man2/setuid.html"><code>setuid</code>(2) / <code>setgid</code>(2)</a></li>
<li><a href="/man2/setresuid.html"><code>setresuid</code>(2) / <code>setresgid</code>(2)</a></li>
</ul>
</body>
</html>