Linux Users and Groups

 

Linux Users and Groups
Chapter 8 Part 1 — Understanding User Identities and /etc/passwd
Part 1
of 4
Beginner
Level
~20 min
Read Time
What You Will Learn

Linux is a multi-user operating system. Every process, every file, and every socket belongs to someone. To enforce this ownership and control access, Linux uses a simple but powerful system of user IDs and group IDs. In this tutorial, you will learn how Linux stores user account information, what each field in the password file means, and how the kernel uses these numbers internally.

By the end of this part, you will be able to read an /etc/passwd entry confidently, understand what a UID and GID actually are at the kernel level, and explain why Linux uses numeric IDs instead of names internally.

Key Concepts in This Tutorial
UID (User ID) GID (Group ID) /etc/passwd Login Name Home Directory Login Shell Superuser (root) File Ownership NIS / LDAP
Why Does Linux Need Users and Groups?

Think of a university computer lab with 200 students sharing the same machine. Student A should not be able to delete Student B’s assignment files. A lab assistant should be able to read everyone’s files for grading, but not overwrite them. The system administrator should have full control over everything.

Linux solves this problem using three mechanisms working together:

User Identity (who you are) Group Membership (which team you belong to) Permissions (what you are allowed to do)

Every process running on Linux carries user and group IDs. When that process tries to open a file, the kernel checks those IDs against the file’s ownership and permission bits to decide whether to allow or deny the access. It is that simple at the core.

Importantly, the kernel works exclusively with numbers — UIDs and GIDs. The human-readable name “alice” is just a convenience for you and me. Internally, the kernel only sees 1001 or whatever numeric ID is mapped to that name.

What Is a UID?

A User ID (UID) is a non-negative integer that uniquely identifies a user on a system. Think of it like your employee badge number at a company. Your name might change (if you get married, for example), but your badge number stays the same.

You can check your own UID from the terminal like this:

$ id
uid=1001(alice) gid=1001(alice) groups=1001(alice),4(adm),27(sudo)

The UID 0 is special — it belongs to the superuser, called root. Any process running with UID 0 bypasses almost all permission checks in the kernel. This is why gaining root access is so powerful and why it must be protected carefully.

On modern Linux (kernel 2.4 and later), UIDs are stored as 32-bit unsigned integers, meaning the range is 0 to about 4 billion. On very old kernels (2.2 and before), it was only 16-bit, limiting UIDs to 65,535.

/* UIDs and GIDs are defined using these typedefs */
#include <sys/types.h>

uid_t uid;   /* User ID  — typically 32-bit unsigned int */
gid_t gid;   /* Group ID — typically 32-bit unsigned int */
What Is a GID?

A Group ID (GID) works the same way as a UID but for groups. A group is simply a named collection of users. For example, a company might have a group called “developers” (GID 200) with UIDs of all the engineers in it. Any file owned by that group can grant read/write permission to all members at once.

A user can belong to multiple groups simultaneously. For example, a senior developer might be in “developers”, “git-users”, and “docker” groups all at once. The primary group (the first one) comes from the password file. Additional group memberships come from the group file, which we cover in Part 2.

/* Get the UID and GID of the current process */
#include <unistd.h>

uid_t getuid(void);   /* Returns real UID of the calling process */
gid_t getgid(void);   /* Returns real GID of the calling process */

/* Example usage */
printf("My UID is: %d\n", getuid());
printf("My GID is: %d\n", getgid());
The Password File: /etc/passwd

The file /etc/passwd is the central user database on a Linux system. It is a plain text file where each line represents one user account. Despite the name, it does not actually store passwords on modern systems (that moved to /etc/shadow for security reasons, covered in Part 2).

Here is what a typical line looks like:

alice:x:1001:1001:Alice Wonderland:/home/alice:/bin/bash

Each line has exactly seven fields separated by colons. Let us break each one down.

Field 1 — Login Name

This is the human-readable username. It must be unique on the system. The login name is what you type when you log in. Programs like ls -l translate the numeric UID back to this name when showing file ownership.

$ ls -l /home/alice/notes.txt
-rw-r--r-- 1 alice developers 1024 May 10 09:00 notes.txt

Here, “alice” is the login name displayed. But internally, the kernel stored and checked the numeric UID 1001. The ls command looked up 1001 in /etc/passwd and printed “alice” for readability.

Field 2 — Encrypted Password

On old UNIX systems, this field held the actual encrypted password. Today, on any system with shadow passwords enabled (which is every modern Linux distro), this field simply contains the letter x. The x is a placeholder that tells the system: “the real password is stored in /etc/shadow, go look there.”

alice:x:1001:1001:Alice Wonderland:/home/alice:/bin/bash
      ^
      This "x" means: real password is in /etc/shadow

If this field is empty, no password is required to log in as this user — a serious security risk if left by mistake. If it contains any other string that is not a valid 13-character DES hash, login is disabled for that account.

Field 3 — User ID (UID)

This is the numeric identifier the kernel actually uses. Once the login process authenticates you, all further access checks use this number, not your name.

/* UID 0 = root (superuser) — bypass most permission checks */
root:x:0:0:root:/root:/bin/bash

/* System accounts use low UIDs (1-999 typically) */
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

/* Regular users start at 1000 on most distros */
alice:x:1000:1000:Alice:/home/alice:/bin/bash
bob:x:1001:1001:Bob:/home/bob:/bin/bash

It is technically possible (though unusual) to have two entries with the same UID. This creates two login names for the same identity — useful for giving a service account a human-friendly alias. Both names see the same files and have the same permissions since the kernel only looks at the UID number.

Field 4 — Primary Group ID (GID)

This is the GID of the user’s primary group. When the user creates a new file, this group is assigned as the file’s group owner by default (unless a setgid bit is set on the directory).

/* When alice (GID 1001) creates a file */
$ touch /tmp/testfile
$ ls -l /tmp/testfile
-rw-r--r-- 1 alice alice 0 May 10 09:05 testfile
                    ^^^^^
                    Group owner = alice's primary group

/* Now imagine alice creates a file inside a shared project directory */
$ ls -l /projects/webapp/
drwxrwsr-x 2 root developers 4096 May 10 08:00 webapp/
            ^setgid bit means new files inherit "developers" group
Field 5 — Comment / GECOS Field

This is a free-text field used to store the user’s full name and optional contact information. The field name “gecos” is historical — it dates back to an old machine called the GE Comprehensive Operating System. Today it just stores descriptive info about the user.

/* Format: Full Name,Room,Work Phone,Home Phone,Other */
alice:x:1001:1001:Alice Wonderland,Lab 3,+91-9876543210:/home/alice:/bin/bash

/* You can view the gecos field using the finger command */
$ finger alice
Login: alice                            Name: Alice Wonderland
Directory: /home/alice                  Shell: /bin/bash
Office: Lab 3, +91-9876543210

Many systems only store the full name here. The other comma-separated sub-fields are optional and often left blank.

Field 6 — Home Directory

When a user logs in, the shell starts in this directory. Linux sets the HOME environment variable to this path automatically. Programs like cd ~ use this variable to find your home folder.

/* After login, HOME is set from /etc/passwd */
$ echo $HOME
/home/alice

/* System accounts often use /var or /usr/sbin as home */
/* and non-login accounts use /nonexistent */
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
Field 7 — Login Shell

This is the program that runs after successful authentication. For interactive users it is typically a shell like bash, zsh, or sh. For service accounts that should never have an interactive login, it is set to /sbin/nologin or /usr/bin/false — these programs just exit immediately, preventing any shell session.

/* Regular user — gets a bash shell on login */
alice:x:1001:1001:Alice:/home/alice:/bin/bash

/* Service account — cannot get an interactive shell */
nginx:x:998:998:nginx web server:/var/cache/nginx:/sbin/nologin

/* Checking which shell you are using */
$ echo $SHELL
/bin/bash

If this field is empty, Linux defaults to /bin/sh (the Bourne shell). The value in this field becomes the SHELL environment variable in your session.

Reading /etc/passwd: A Complete Example

Let us put it all together. Here is a snippet from a typical /etc/passwd file with annotations:

# Format: name:password:UID:GID:comment:home:shell

root:x:0:0:root:/root:/bin/bash
# UID=0, superuser account, home is /root

daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
# System daemon, no interactive login allowed

alice:x:1001:1001:Alice Kumar,Desk 5:/home/alice:/bin/bash
# Regular user, UID 1001, has bash shell

bob:x:1002:1002:Bob Sharma:/home/bob:/bin/zsh
# Bob prefers zsh as his login shell

jenkins:x:1003:1003:Jenkins CI:/var/lib/jenkins:/bin/false
# CI build system, no shell access

You can print the file yourself on any Linux system:

$ cat /etc/passwd

Or look at just your own entry:

$ grep "^$(whoami):" /etc/passwd
alice:x:1001:1001:Alice Kumar,Desk 5:/home/alice:/bin/bash
NIS and LDAP: When /etc/passwd Is Not Enough

In large organizations with hundreds of machines, maintaining a separate /etc/passwd file on every server would be a nightmare. If alice changes her password, would you update every single machine manually?

Network Information System (NIS) and Lightweight Directory Access Protocol (LDAP) solve this by storing user information centrally on a server. Every machine in the network queries that server instead of reading a local file.

The important point for programmers is: the library functions (getpwnam, getpwuid, etc. — covered in Part 3) abstract this away completely. Your C program does not need to know whether the user info is coming from a local file, NIS, or LDAP. The Name Service Switch (NSS) handles that transparently.

/* This code works regardless of whether you use local files,
   NIS, or LDAP — the library handles the backend */
#include <pwd.h>
#include <stdio.h>

int main() {
    struct passwd *p = getpwnam("alice");
    if (p != NULL)
        printf("Alice's UID = %d\n", p->pw_uid);
    return 0;
}
Quick Exercises to Reinforce Learning
Run: cat /etc/passwd and identify all 7 fields for your own account Run: id and compare the UIDs/GIDs you see with /etc/passwd Find a system account (UID under 100) and see what shell it uses Try: grep nologin /etc/passwd and count the service accounts Use awk to print only the username and home directory columns
/* awk one-liner to print username and home directory */
$ awk -F: '{print $1, "->", $6}' /etc/passwd

root -> /root
daemon -> /usr/sbin
alice -> /home/alice
bob -> /home/bob
Continue to Part 2

Next, we dive into the shadow password file (/etc/shadow) and the group file (/etc/group) — including why passwords were moved to a separate file and how group memberships are defined.

Part 2: Shadow and Group Files Back to Home

Leave a Reply

Your email address will not be published. Required fields are marked *