]>
oss.titaniummirror.com Git - repo_shell.git/blob - repo_shell.c
15 #include "stringutils.h"
17 #define CFG_FILE "/etc/repo_shell.conf"
18 #define SHELL "/bin/bash"
19 #define GIT_ACL_FILE ".gitacls"
21 enum { REPO_UMASK
= 027 };
29 bool allow_interactive
;
34 /* This is the function for which setuid root is needed for repo_shell */
35 static void change_user(char *user
)
37 struct passwd
*pw
= getpwnam(user
);
40 die("invalid user %s", user
);
45 /* Set the user and group permissions back to the requesting user */
46 static void reset_user()
52 static char *dequote(char *arg
)
56 if (arg
&& *arg
== '\'') {
57 char* end
= arg
+ strlen(arg
) - 1;
59 if (end
!= arg
&& *end
== '\'') {
67 static char *add_prefix(const char *prefix
, const char* arg
)
72 if (arg
&& prefix
&& (i
= strlen(prefix
))) {
73 narg
= xmalloc(sizeof(char *) * (i
+ strlen(arg
) + 2));
75 strcpy(narg
+ i
++, "/");
76 strcpy(narg
+ i
, arg
);
81 /* Return true if the user's permissions >= those required */
82 static bool git_check_access(const char *cmd
, const char *repo
,
85 /* What access is required per the incoming command? Uploading commands
86 * require PERMS_READ_WRITE, while all other commands are assumed to need only
89 perms_t need
= !strncmp(cmd
, "git-upload", 10) ? PERMS_READ
:
91 perms_t have
= git_acl(user
, repo
, cfg
.git_acl_file
);
95 static int do_git_cmd(const char *cmd
, char *arg
, char *user
)
101 if (!(arg
= dequote(arg
)))
103 if (strncmp(cmd
, "git-", 4))
106 change_user(cfg
.owner
);
108 if (!git_check_access(cmd
, arg
, user
))
109 die("insufficient ACL permissions");
112 nargv
[1] = add_prefix(cfg
.git_root
, arg
);
115 ret
= execvp(nargv
[0], (char *const *) nargv
);
116 /* Code unreached if execv successful */
117 free((char*)nargv
[1]);
122 static int do_svnserve_cmd(const char *cmd
, char *arg
, char *user
)
124 const char *svnserve_argv
[7] = {
125 cmd
, "-t", "--root", cfg
.svn_root
, "--tunnel-user", user
, NULL
129 change_user(cfg
.owner
);
131 return execvp(svnserve_argv
[0], (char *const *) svnserve_argv
);
134 static void cd_to_homedir(void)
136 const char *home
= getenv("HOME");
138 die("user variable HOME is unset");
139 if (chdir(home
) == -1)
140 die("could not chdir to user's home directory");
143 static struct commands
{
145 int (*exec
)(const char *cmd
, char *arg
, char *user
);
147 { "git-receive-pack", do_git_cmd
},
148 { "git-upload-pack", do_git_cmd
},
149 { "git-upload-archive", do_git_cmd
},
150 { "svnserve", do_svnserve_cmd
},
154 static int ini_handler(void* user
, const char* section
, const char* name
,
157 cfg_t
* pconfig
= (cfg_t
*)user
;
159 if (!strcmp(name
, "svn_root"))
160 pconfig
->svn_root
= xstrdup(value
);
161 else if (!strcmp(name
, "git_root")) {
162 pconfig
->git_root
= xstrdup(value
);
163 pconfig
->git_acl_file
= add_prefix(value
, GIT_ACL_FILE
);
164 } else if (!strcmp(name
, "owner"))
165 pconfig
->owner
= xstrdup(value
);
166 else if (!strcmp(name
, "allow_interactive"))
167 pconfig
->allow_interactive
= str_has_word(value
, pconfig
->user
);
169 return 0; /* unknown section/name, error */
173 int main(int argc
, char **argv
)
176 const char **user_argv
;
177 struct commands
*cmd
;
183 * Always open file descriptors 0/1/2 to avoid clobbering files
184 * in die(). It also avoids not messing up when the pipes are
185 * dup'ed onto stdin/stdout/stderr in the child processes we spawn.
187 devnull_fd
= open("/dev/null", O_RDWR
);
188 while (devnull_fd
>= 0 && devnull_fd
<= 2)
189 devnull_fd
= dup(devnull_fd
);
190 if (devnull_fd
== -1)
191 die("opening /dev/null failed");
194 if (argc
== 2 && (!strcmp(argv
[1], "-h") || !strcmp(argv
[1], "--help"))) {
195 fprintf(stderr
, "%s is a replacement login shell.\n"
196 " May be ran from the command line with one of these options:\n"
197 " -h|--help this text\n"
198 " -v|--version program version string\n"
199 " -t|--test <user> <repo> test access\n"
200 " -d|--detail <user> <repo> test access, outputting more detail\n"
205 if (argc
== 2 && (!strcmp(argv
[1], "-v") || !strcmp(argv
[1], "--version"))) {
206 fprintf(stderr
, "%s\n", version
);
210 pw
= getpwuid(getuid());
211 cfg
.user
= xstrdup(pw
->pw_name
);
212 if (ini_parse(CFG_FILE
, ini_handler
, &cfg
) < 0)
213 die("cannot read config file %s", CFG_FILE
);
216 if (!cfg
.allow_interactive
) {
217 fprintf(stderr
, "\n");
218 die("only repository access is allowed");
222 execvp(argv
[0], (char *const *) argv
);
226 if ((!strcmp(argv
[1], "-d") || !strcmp(argv
[1], "--detail"))) {
230 die("usage: %s -d|--detail <user> <repo>", argv
[0]);
231 p
= git_acl(argv
[2], argv
[3], cfg
.git_acl_file
);
233 "user '%s' repo '%s' perms '%s'\n via userid '%s' repoid '%s'\n",
234 argv
[2], argv
[3], git_acl_perms_as_str(p
), git_acl_last_userid(),
235 git_acl_last_repoid());
239 if ((!strcmp(argv
[1], "-t") || !strcmp(argv
[1], "--test"))) {
243 die("usage: %s -t|--test <user> <repo>", argv
[0]);
244 p
= git_acl(argv
[2], argv
[3], cfg
.git_acl_file
);
245 printf("%s\n", git_acl_perms_as_str(p
));
250 /* argv[0] = repo_shell, argv[1] = -c, argv[2] = cmd
251 * cmd = "svnserve -t" or "git-xxx '/path/to/repo.git'"
253 prog
= xstrdup(argv
[2]);
254 if (!strncmp(prog
, "git", 3) && isspace(prog
[3]))
255 /* Accept "git foo" as if the caller said "git-foo". */
258 for (cmd
= cmd_list
; cmd
->name
; cmd
++) {
259 int len
= strlen(cmd
->name
);
261 if (strncmp(cmd
->name
, prog
, len
))
269 arg
= prog
+ len
+ 1;
275 exit(cmd
->exec(cmd
->name
, arg
, cfg
.user
));
279 if (!cfg
.allow_interactive
)
280 die("only repository access is allowed");
284 execvp(argv
[0], (char *const *) argv
);