mirror of
https://github.com/javalsai/lidm.git
synced 2025-08-31 02:18:00 +02:00
feat: exec parser for desktop entries
Feature-rich Exec= parser implementation for .desktop files * backslash stripping (e.g '\ ' -> ' ') * percentage stripping (e.g '%u' -> '', '%%' -> '%') * quote string handling (e.g 'arg1 "arg 2"' -> "arg1", "arg 2") The current implementation strips all "percentage codes", instead of handling them. Argument count is limited at 100.
This commit is contained in:
197
src/auth.c
197
src/auth.c
@@ -15,6 +15,185 @@
|
|||||||
#include "unistd.h"
|
#include "unistd.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
// constants for exec string parsing
|
||||||
|
// ARG_LENGTH is the initial length of a parsed argument
|
||||||
|
#define MAX_ARGS 100
|
||||||
|
#define ARG_LENGTH 64
|
||||||
|
|
||||||
|
// parse Exec=/bin/prog arg1 arg2\ with\ spaces
|
||||||
|
void free_parsed_args(char** args, int arg_count) {
|
||||||
|
if (!args) return;
|
||||||
|
for (int i = 0; i < arg_count; i++) {
|
||||||
|
free(args[i]);
|
||||||
|
}
|
||||||
|
free((void*)args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* small closure-like struct to pass state to helper functions */
|
||||||
|
struct ctx {
|
||||||
|
char** pcur;
|
||||||
|
size_t* pcur_len;
|
||||||
|
size_t* pcur_cap;
|
||||||
|
char*** pargv;
|
||||||
|
int* pargc;
|
||||||
|
};
|
||||||
|
/* append_char(state, ch) -> 0 on error, 1 on success */
|
||||||
|
int append_char(struct ctx* st, char ch) {
|
||||||
|
char** pcur = st->pcur;
|
||||||
|
size_t* plen = st->pcur_len;
|
||||||
|
size_t* pcap = st->pcur_cap;
|
||||||
|
if (*plen + 1 >= *pcap) {
|
||||||
|
size_t newcap = *pcap ? (*pcap) * 2 : ARG_LENGTH;
|
||||||
|
char* cur = (char*)realloc(*pcur, newcap);
|
||||||
|
if (!cur) return 0;
|
||||||
|
*pcur = cur;
|
||||||
|
*pcap = newcap;
|
||||||
|
}
|
||||||
|
(*pcur)[(*plen)++] = ch;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* push_arg(state) -> 0 on error, 1 on success */
|
||||||
|
int push_arg(struct ctx* st) {
|
||||||
|
char** pcur = st->pcur;
|
||||||
|
size_t* plen = st->pcur_len;
|
||||||
|
size_t* pcap = st->pcur_cap;
|
||||||
|
char*** pargv = st->pargv;
|
||||||
|
int* pargc = st->pargc;
|
||||||
|
|
||||||
|
if (*pargc > MAX_ARGS) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!*pcur) {
|
||||||
|
char* empty = strdup("");
|
||||||
|
if (!empty) return 0;
|
||||||
|
char** na = (char**)realloc((void*)*pargv, sizeof(char*) * ((*pargc) + 1));
|
||||||
|
if (!na) {
|
||||||
|
free(empty);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*pargv = na;
|
||||||
|
(*pargv)[(*pargc)++] = empty;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!append_char(st, '\0')) return 0;
|
||||||
|
char* final = (char*)realloc(*pcur, *plen);
|
||||||
|
if (!final) final = *pcur;
|
||||||
|
*pcur = NULL;
|
||||||
|
*plen = 0;
|
||||||
|
*pcap = 0;
|
||||||
|
char** na = (char**)realloc((void*)*pargv, sizeof(char*) * ((*pargc) + 1));
|
||||||
|
if (!na) {
|
||||||
|
free(final);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*pargv = na;
|
||||||
|
(*pargv)[(*pargc)++] = final;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return codes:
|
||||||
|
0 = success
|
||||||
|
1 = bad args
|
||||||
|
2 = memory
|
||||||
|
3 = syntax
|
||||||
|
|
||||||
|
Important: call free_parsed_args afterwards to free the passed ***args
|
||||||
|
*/
|
||||||
|
// NOLINTBEGIN(readability-function-cognitive-complexity)
|
||||||
|
int parse_exec_string(const char* exec_s, char*** args, int* arg_count) {
|
||||||
|
if (!exec_s || !args || !arg_count) return 1;
|
||||||
|
*args = NULL;
|
||||||
|
*arg_count = 0;
|
||||||
|
|
||||||
|
size_t len = strlen(exec_s);
|
||||||
|
size_t idx = 0;
|
||||||
|
char* cur = NULL;
|
||||||
|
size_t cur_len = 0;
|
||||||
|
size_t cur_cap = 0;
|
||||||
|
char** argv = NULL;
|
||||||
|
int argc = 0;
|
||||||
|
int in_quote = 0;
|
||||||
|
|
||||||
|
struct ctx ctx;
|
||||||
|
ctx.pcur = &cur;
|
||||||
|
ctx.pcur_len = &cur_len;
|
||||||
|
ctx.pcur_cap = &cur_cap;
|
||||||
|
ctx.pargv = &argv;
|
||||||
|
ctx.pargc = &argc;
|
||||||
|
|
||||||
|
while (idx < len) {
|
||||||
|
char cur_c = exec_s[idx];
|
||||||
|
if (!in_quote && (cur_c == ' ' || cur_c == '\t' || cur_c == '\n')) {
|
||||||
|
if (cur_cap) {
|
||||||
|
if (!push_arg(&ctx)) goto nomem;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!in_quote && cur_c == '"') {
|
||||||
|
in_quote = 1;
|
||||||
|
idx++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (in_quote && cur_c == '"') {
|
||||||
|
in_quote = 0;
|
||||||
|
idx++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_c == '\\') {
|
||||||
|
if (idx + 1 >= len) goto syntax_err;
|
||||||
|
if (!append_char(&ctx, exec_s[idx + 1])) goto nomem;
|
||||||
|
idx += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_c == '%') {
|
||||||
|
if (idx + 1 >= len) goto syntax_err;
|
||||||
|
if (exec_s[idx + 1] == '%') {
|
||||||
|
if (!append_char(&ctx, '%')) goto nomem;
|
||||||
|
idx += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* drop any %X */
|
||||||
|
idx += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!append_char(&ctx, cur_c)) goto nomem;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_quote) goto syntax_err;
|
||||||
|
if (cur_cap) {
|
||||||
|
if (!push_arg(&ctx)) goto nomem;
|
||||||
|
}
|
||||||
|
char** na = (char**)realloc((void*)argv, sizeof(char*) * (argc + 1));
|
||||||
|
if (!na) goto nomem;
|
||||||
|
argv = na;
|
||||||
|
argv[argc] = NULL;
|
||||||
|
|
||||||
|
*args = argv;
|
||||||
|
*arg_count = argc;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nomem:
|
||||||
|
if (cur) free(cur);
|
||||||
|
free_parsed_args(argv, argc);
|
||||||
|
*args = NULL;
|
||||||
|
*arg_count = 0;
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
syntax_err:
|
||||||
|
if (cur) free(cur);
|
||||||
|
free_parsed_args(argv, argc);
|
||||||
|
*args = NULL;
|
||||||
|
*arg_count = 0;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
// NOLINTEND(readability-function-cognitive-complexity)
|
||||||
|
|
||||||
int pam_conversation(int num_msg, const struct pam_message** msg,
|
int pam_conversation(int num_msg, const struct pam_message** msg,
|
||||||
struct pam_response** resp, void* appdata_ptr) {
|
struct pam_response** resp, void* appdata_ptr) {
|
||||||
struct pam_response* reply =
|
struct pam_response* reply =
|
||||||
@@ -237,19 +416,29 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
|
|||||||
|
|
||||||
*reach_session = true;
|
*reach_session = true;
|
||||||
|
|
||||||
// TODO: these will be different due to TryExec
|
// TODO: test existence of executable with TryExec
|
||||||
// and, Exec/TryExec might contain spaces as args
|
char** args = NULL;
|
||||||
|
int arg_count = 0;
|
||||||
|
int parse_status = parse_exec_string(session.exec, &args, &arg_count);
|
||||||
|
if (parse_status != 0 || arg_count == 0 || !args[0]) {
|
||||||
|
(void)fprintf(stderr, "failure parsing exec string '%s': %d\n",
|
||||||
|
session.exec ? session.exec : "NULL", parse_status);
|
||||||
|
free_parsed_args(args, arg_count);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
printf("\x1b[0m");
|
printf("\x1b[0m");
|
||||||
// NOLINTNEXTLINE(bugprone-branch-clone)
|
// NOLINTNEXTLINE(bugprone-branch-clone)
|
||||||
if (session.type == SHELL) {
|
if (session.type == SHELL) {
|
||||||
clear_screen();
|
clear_screen();
|
||||||
(void)fflush(stdout);
|
(void)fflush(stdout);
|
||||||
execlp(session.exec, session.exec, NULL);
|
execvp(args[0], args);
|
||||||
} else if (session.type == XORG || session.type == WAYLAND) {
|
} else if (session.type == XORG || session.type == WAYLAND) {
|
||||||
clear_screen();
|
clear_screen();
|
||||||
(void)fflush(stdout);
|
(void)fflush(stdout);
|
||||||
execlp(session.exec, session.exec, NULL);
|
execvp(args[0], args);
|
||||||
}
|
}
|
||||||
|
free_parsed_args(args, arg_count);
|
||||||
perror("execl error");
|
perror("execl error");
|
||||||
(void)fputs("failure calling session\n", stderr);
|
(void)fputs("failure calling session\n", stderr);
|
||||||
} else {
|
} else {
|
||||||
|
Reference in New Issue
Block a user