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