1 /******************************************************************************* 2 3 Config extension to drop privileges. 4 5 Copyright: 6 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 7 All rights reserved. 8 9 License: 10 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 11 Alternatively, this file may be distributed under the terms of the Tango 12 3-Clause BSD License (see LICENSE_BSD.txt for details). 13 14 *******************************************************************************/ 15 16 module ocean.util.app.ext.DropPrivilegesExt; 17 18 19 import ocean.core.array.Mutation; 20 import ocean.transition; 21 import ocean.text.util.StringC; 22 import ConfigFiller = ocean.util.config.ConfigFiller; 23 import ocean.util.app.ext.model.IConfigExtExtension; 24 import ocean.util.app.model.IApplicationExtension; 25 26 import core.stdc.errno; 27 import core.stdc.string; 28 import core.sys.posix.grp; 29 import core.sys.posix.pwd; 30 import core.sys.posix.unistd; 31 32 33 /******************************************************************************* 34 35 Config extension to drop privileges files. 36 37 User and group must be specified in the configfile under the PERMISSION 38 section. If root starts the program, it will drop privileges after 39 reading the config. If it is started with the configured user, 40 nothing happens. If it is started as a different user, it will exit 41 with an error. 42 43 Config example: 44 ---- 45 [PERMISSIONS] 46 user = john 47 group = john 48 ---- 49 50 *******************************************************************************/ 51 52 class DropPrivilegesExt : IConfigExtExtension 53 { 54 static class Config 55 { 56 /*********************************************************************** 57 58 User to run as, mandatory setting 59 60 ***********************************************************************/ 61 62 ConfigFiller.Required!(istring) user; 63 64 /*********************************************************************** 65 66 Group to run as, mandatory setting 67 68 ***********************************************************************/ 69 70 ConfigFiller.Required!(istring) group; 71 } 72 73 74 /*************************************************************************** 75 76 Extension order. This extension uses -5_000 because it should be 77 called pretty early, but after the ConfigExt extension. 78 79 Returns: 80 the extension order 81 82 ***************************************************************************/ 83 84 public override int order ( ) 85 { 86 return -5000; 87 } 88 89 90 /*************************************************************************** 91 92 Function executed before the program runs. 93 94 Params: 95 app = the application instance that will run 96 config = configuration instance to be filled 97 98 ***************************************************************************/ 99 100 override void processConfig ( IApplication app, ConfigParser config ) 101 { 102 auto conf = ConfigFiller.fill!(Config)("PERMISSIONS", config); 103 104 if ( conf.group() == "root" ) throw new Exception("Group can not be root!"); 105 if ( conf.user() == "root" ) throw new Exception("User can not be root!"); 106 107 setGroup(conf.group()); 108 setUser(conf.user()); 109 } 110 111 112 /*************************************************************************** 113 114 Change user permissions to usr 115 116 Params: 117 usr = User to become 118 119 ***************************************************************************/ 120 121 private void setUser ( istring usr ) 122 { 123 passwd* result; 124 passwd passwd_buf; 125 static mstring user_buf; 126 char[2048] buf; 127 128 user_buf.copy(usr); 129 auto res = getpwnam_r(StringC.toCString(user_buf), &passwd_buf, 130 buf.ptr, buf.length, &result); 131 132 if ( result == null ) 133 { 134 if ( res == 0 ) 135 { 136 throw new Exception("User " ~ usr ~ " not found!"); 137 } 138 else 139 { 140 char* err = strerror(res); 141 auto msg = "Error while getting user " ~ usr ~ 142 ": " ~ StringC.toDString(err); 143 throw new Exception(assumeUnique(msg)); 144 } 145 } 146 147 if ( result.pw_uid == geteuid() ) return; 148 149 res = setuid(result.pw_uid); 150 151 if ( res != 0 ) 152 { 153 char* err = strerror(errno()); 154 auto msg = "Failed to set process user id to " ~ usr 155 ~ ": " ~ StringC.toDString(err); 156 throw new Exception(assumeUnique(msg)); 157 } 158 } 159 160 161 /*************************************************************************** 162 163 Change group permissions to grp 164 165 Params: 166 grp = Group to become 167 168 ***************************************************************************/ 169 170 private void setGroup ( istring grp ) 171 { 172 group* result; 173 group group_buf; 174 static mstring grp_buf; 175 char[2048] buf; 176 177 grp_buf.copy(grp); 178 auto res = getgrnam_r(StringC.toCString(grp_buf), &group_buf, 179 buf.ptr, buf.length, &result); 180 181 if ( result == null ) 182 { 183 if ( res == 0 ) 184 { 185 throw new Exception("Group " ~ grp ~ " not found!"); 186 } 187 else 188 { 189 char* err = strerror(res); 190 auto msg = "Error while getting group " ~ grp ~ 191 ": " ~ StringC.toDString(err); 192 throw new Exception(assumeUnique(msg)); 193 } 194 } 195 196 if ( result.gr_gid == getegid() ) return; 197 198 res = setgid(result.gr_gid); 199 200 if ( res != 0 ) 201 { 202 char* err = strerror(errno()); 203 auto msg = "Failed to set process user group to " ~ grp 204 ~ ": " ~ StringC.toDString(err); 205 throw new Exception(assumeUnique(msg)); 206 } 207 } 208 209 /*************************************************************************** 210 211 Function executed before the configuration files are parsed. 212 Only present to satisfy the interface 213 214 Params: 215 app = application instance 216 config = configuration parser 217 218 ***************************************************************************/ 219 220 override void preParseConfig ( IApplication app, ConfigParser config ) {} 221 222 223 /*************************************************************************** 224 225 Function to filter the list of configuration files to parse. 226 Only present to satisfy the interface 227 228 Params: 229 app = application instance 230 config = configuration parser 231 files = current list of configuration files to parse 232 233 Returns: 234 new list of configuration files to parse 235 236 ***************************************************************************/ 237 238 override istring[] filterConfigFiles ( IApplication app, ConfigParser config, 239 istring[] files ) { return files; } 240 }